From b50e88ebdf375cf81ad63586ce4599979262f975 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Sat, 28 Nov 2015 16:32:24 +0900 Subject: Make `assert_recognizes` to traverse mounted engines Before this commit paths of mounted engines are not traversed when `assert_recognizes` is called, causing strange test results. This commit enable to traverse mounted paths. --- actionpack/CHANGELOG.md | 4 ++ actionpack/lib/action_dispatch/routing/endpoint.rb | 10 +-- .../lib/action_dispatch/routing/inspector.rb | 4 +- .../lib/action_dispatch/routing/route_set.rb | 7 +++ .../test/dispatch/routing_assertions_test.rb | 71 ++++++++++++++++++++++ 5 files changed, 90 insertions(+), 6 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 370e3a1958..7eb56e596e 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Make `assert_recognizes` to traverse mounted engines + + *Yuichiro Kaneko* + * Add extension synonyms `yml` and `yaml` for MIME type `application/x-yaml`. *bogdanvlviv* diff --git a/actionpack/lib/action_dispatch/routing/endpoint.rb b/actionpack/lib/action_dispatch/routing/endpoint.rb index 88aa13c3e8..819305615e 100644 --- a/actionpack/lib/action_dispatch/routing/endpoint.rb +++ b/actionpack/lib/action_dispatch/routing/endpoint.rb @@ -1,10 +1,12 @@ module ActionDispatch module Routing class Endpoint # :nodoc: - def dispatcher?; false; end - def redirect?; false; end - def matches?(req); true; end - def app; self; end + def dispatcher?; false; end + def redirect?; false; end + def engine?; rack_app.respond_to?(:routes); end + def matches?(req); true; end + def app; self; end + def rack_app; app; end end end end diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index 5d30a545a2..4e859fbac3 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -13,7 +13,7 @@ module ActionDispatch end def rack_app - app.app + app.rack_app end def path @@ -45,7 +45,7 @@ module ActionDispatch end def engine? - rack_app.respond_to?(:routes) + app.engine? end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 16237bd564..d8df247068 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -751,6 +751,10 @@ module ActionDispatch end req = make_request(env) + recognize_path_with_request(req, path, extras) + end + + def recognize_path_with_request(req, path, extras) @router.recognize(req) do |route, params| params.merge!(extras) params.each do |key, value| @@ -770,6 +774,9 @@ module ActionDispatch end return req.path_parameters + elsif app.matches?(req) && app.engine? + path_parameters = app.rack_app.routes.recognize_path_with_request(req, path, extras) + return path_parameters end end diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb index 56ea644f22..111d5d637f 100644 --- a/actionpack/test/dispatch/routing_assertions_test.rb +++ b/actionpack/test/dispatch/routing_assertions_test.rb @@ -1,13 +1,39 @@ require 'abstract_unit' +require 'rails/engine' require 'controller/fake_controllers' class SecureArticlesController < ArticlesController; end class BlockArticlesController < ArticlesController; end class QueryArticlesController < ArticlesController; end +class SecureBooksController < BooksController; end +class BlockBooksController < BooksController; end +class QueryBooksController < BooksController; end + class RoutingAssertionsTest < ActionController::TestCase def setup + engine = Class.new(Rails::Engine) do + def self.name + "blog_engine" + end + end + engine.routes.draw do + resources :books + + scope 'secure', :constraints => { :protocol => 'https://' } do + resources :books, :controller => 'secure_books' + end + + scope 'block', :constraints => lambda { |r| r.ssl? } do + resources :books, :controller => 'block_books' + end + + scope 'query', :constraints => lambda { |r| r.params[:use_query] == 'true' } do + resources :books, :controller => 'query_books' + end + end + @routes = ActionDispatch::Routing::RouteSet.new @routes.draw do resources :articles @@ -23,6 +49,8 @@ class RoutingAssertionsTest < ActionController::TestCase scope 'query', :constraints => lambda { |r| r.params[:use_query] == 'true' } do resources :articles, :controller => 'query_articles' end + + mount engine => "/shelf" end end @@ -82,6 +110,49 @@ class RoutingAssertionsTest < ActionController::TestCase assert_match err.message, "This is a really bad msg" end + def test_assert_recognizes_with_engine + assert_recognizes({ :controller => 'books', :action => 'index' }, '/shelf/books') + assert_recognizes({ :controller => 'books', :action => 'show', :id => '1' }, '/shelf/books/1') + end + + def test_assert_recognizes_with_engine_and_extras + assert_recognizes({ :controller => 'books', :action => 'index', :page => '1' }, '/shelf/books', { :page => '1' }) + end + + def test_assert_recognizes_with_engine_and_method + assert_recognizes({ :controller => 'books', :action => 'create' }, { :path => '/shelf/books', :method => :post }) + assert_recognizes({ :controller => 'books', :action => 'update', :id => '1' }, { :path => '/shelf/books/1', :method => :put }) + end + + def test_assert_recognizes_with_engine_and_hash_constraint + assert_raise(Assertion) do + assert_recognizes({ :controller => 'secure_books', :action => 'index' }, 'http://test.host/shelf/secure/books') + end + assert_recognizes({ :controller => 'secure_books', :action => 'index', :protocol => 'https://' }, 'https://test.host/shelf/secure/books') + end + + def test_assert_recognizes_with_engine_and_block_constraint + assert_raise(Assertion) do + assert_recognizes({ :controller => 'block_books', :action => 'index' }, 'http://test.host/shelf/block/books') + end + assert_recognizes({ :controller => 'block_books', :action => 'index' }, 'https://test.host/shelf/block/books') + end + + def test_assert_recognizes_with_engine_and_query_constraint + assert_raise(Assertion) do + assert_recognizes({ :controller => 'query_books', :action => 'index', :use_query => 'false' }, '/shelf/query/books', { :use_query => 'false' }) + end + assert_recognizes({ :controller => 'query_books', :action => 'index', :use_query => 'true' }, '/shelf/query/books', { :use_query => 'true' }) + end + + def test_assert_recognizes_raises_message_with_engine + err = assert_raise(Assertion) do + assert_recognizes({ :controller => 'secure_books', :action => 'index' }, 'http://test.host/shelf/secure/books', {}, "This is a really bad msg") + end + + assert_match err.message, "This is a really bad msg" + end + def test_assert_routing assert_routing('/articles', :controller => 'articles', :action => 'index') end -- cgit v1.2.3 From 46d0bbdbdeedc1001922858f49d1caa9785e0345 Mon Sep 17 00:00:00 2001 From: James Coleman Date: Tue, 4 Oct 2016 15:22:27 -0400 Subject: Add test validating that Model.attribute_names cache is busted --- activerecord/test/cases/attributes_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb index f4620ae2da..5874a09f86 100644 --- a/activerecord/test/cases/attributes_test.rb +++ b/activerecord/test/cases/attributes_test.rb @@ -106,12 +106,14 @@ module ActiveRecord assert_equal 6, klass.attribute_types.length assert_equal 6, klass.column_defaults.length + assert_equal 6, klass.attribute_names.length assert_not klass.attribute_types.include?("wibble") klass.attribute :wibble, Type::Value.new assert_equal 7, klass.attribute_types.length assert_equal 7, klass.column_defaults.length + assert_equal 7, klass.attribute_names.length assert_includes klass.attribute_types, "wibble" end -- cgit v1.2.3 From 3360742396a00c9e2ff9838373788bed432d5ea7 Mon Sep 17 00:00:00 2001 From: Devin Christensen Date: Wed, 12 Apr 2017 14:45:10 -0600 Subject: Switch to LIFO for the connection pool Using a FIFO for the connection pool can lead to issues when there are upstream components (pgbouncer, haproxy, etc.) that terminate connections that are idle after a period of time. Switching to a LIFO reduces the probability that a thread will checkout a connection that is about to be closed by an idle timeout in an upstream component. --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 53dbbd8c21..ea76ff983e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -80,7 +80,7 @@ module ActiveRecord # * private methods that require being called in a +synchronize+ blocks # are now explicitly documented class ConnectionPool - # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool + # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool # with which it shares a Monitor. But could be a generic Queue. # # The Queue in stdlib's 'thread' could replace this class except @@ -111,7 +111,7 @@ module ActiveRecord # Add +element+ to the queue. Never blocks. def add(element) synchronize do - @queue.push element + @queue.unshift element @cond.signal end end -- cgit v1.2.3 From 6116d7bc052839646f448b8403a7287f52b97ed7 Mon Sep 17 00:00:00 2001 From: Devin Christensen Date: Thu, 13 Apr 2017 13:56:42 -0600 Subject: Improve documentation and add test --- .../connection_adapters/abstract/connection_pool.rb | 11 ++++------- activerecord/test/cases/connection_pool_test.rb | 8 ++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index ea76ff983e..3dc265622d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -80,11 +80,8 @@ module ActiveRecord # * private methods that require being called in a +synchronize+ blocks # are now explicitly documented class ConnectionPool - # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool - # with which it shares a Monitor. But could be a generic Queue. - # - # The Queue in stdlib's 'thread' could replace this class except - # stdlib's doesn't support waiting with a timeout. + # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool + # with which it shares a Monitor. class Queue def initialize(lock = Monitor.new) @lock = lock @@ -111,7 +108,7 @@ module ActiveRecord # Add +element+ to the queue. Never blocks. def add(element) synchronize do - @queue.unshift element + @queue.push element @cond.signal end end @@ -173,7 +170,7 @@ module ActiveRecord # Removes and returns the head of the queue if possible, or +nil+. def remove - @queue.shift + @queue.pop end # Remove and return the head the queue if the number of diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 7e88c9cf7a..46081cc13d 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -203,6 +203,14 @@ module ActiveRecord end.join end + def test_checkout_order_is_fifo + conn1 = @pool.checkout + conn2 = @pool.checkout + @pool.checkin conn1 + @pool.checkin conn2 + assert_equal [conn2, conn1], 2.times.map { @pool.checkout } + end + # The connection pool is "fair" if threads waiting for # connections receive them in the order in which they began # waiting. This ensures that we don't timeout one HTTP request -- cgit v1.2.3 From 47d678d9b2ab6d3e888691e8327e657ff0b5dcb0 Mon Sep 17 00:00:00 2001 From: Devin Christensen Date: Thu, 13 Apr 2017 14:05:39 -0600 Subject: Fix typos --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 2 +- activerecord/test/cases/connection_pool_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 3dc265622d..9f660a419d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -80,7 +80,7 @@ module ActiveRecord # * private methods that require being called in a +synchronize+ blocks # are now explicitly documented class ConnectionPool - # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool + # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool # with which it shares a Monitor. class Queue def initialize(lock = Monitor.new) diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 46081cc13d..790eed6007 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -203,7 +203,7 @@ module ActiveRecord end.join end - def test_checkout_order_is_fifo + def test_checkout_order_is_lifo conn1 = @pool.checkout conn2 = @pool.checkout @pool.checkin conn1 -- cgit v1.2.3 From 11e32c1d841c08bd85842eb059fbf30536e804dc Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Mon, 24 Apr 2017 23:26:13 +0100 Subject: Enable query cache on all connection pools Since the query cache no longer eagerly checks out a connection, we can enable it on all connection pools at the start of every request, and it will only take effect for requests that actually use those pools. --- activerecord/lib/active_record/query_cache.rb | 15 +++++++++------ activerecord/test/cases/query_cache_test.rb | 9 +++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb index ec246e97bc..92dd52b068 100644 --- a/activerecord/lib/active_record/query_cache.rb +++ b/activerecord/lib/active_record/query_cache.rb @@ -24,16 +24,19 @@ module ActiveRecord end def self.run - caching_pool = ActiveRecord::Base.connection_pool - caching_was_enabled = caching_pool.query_cache_enabled + ActiveRecord::Base.connection_handler.connection_pool_list.map do |pool| + caching_was_enabled = pool.query_cache_enabled - caching_pool.enable_query_cache! + pool.enable_query_cache! - [caching_pool, caching_was_enabled] + [pool, caching_was_enabled] + end end - def self.complete((caching_pool, caching_was_enabled)) - caching_pool.disable_query_cache! unless caching_was_enabled + def self.complete(caching_pools) + caching_pools.each do |pool, caching_was_enabled| + pool.disable_query_cache! unless caching_was_enabled + end ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool| pool.release_connection if pool.active_connection? && !pool.connection.transaction_open? diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 494663eb04..210199c977 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -426,6 +426,15 @@ class QueryCacheTest < ActiveRecord::TestCase end end + def test_query_cache_is_enabled_on_all_connection_pools + middleware { + ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool| + assert pool.query_cache_enabled + assert pool.connection.query_cache_enabled + end + }.call({}) + end + private def middleware(&app) executor = Class.new(ActiveSupport::Executor) -- cgit v1.2.3 From fa487763d98ccf9c3e66fdb44f09af5c37a50fe5 Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Tue, 12 Apr 2016 02:41:06 +0530 Subject: Changed default behaviour of `ActiveSupport::SecurityUtils.secure_compare`, to make it not leak length information even for variable length string. Renamed old `ActiveSupport::SecurityUtils.secure_compare` to `fixed_length_secure_compare`, and started raising `ArgumentError` in case of length mismatch of passed strings. --- .../action_controller/metal/http_authentication.rb | 11 ++++------- .../metal/request_forgery_protection.rb | 4 ++-- activesupport/CHANGELOG.md | 8 ++++++++ activesupport/lib/active_support/security_utils.rb | 22 ++++++++++++---------- activesupport/test/security_utils_test.rb | 11 +++++++++++ 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index d8bc895265..09df39db1f 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -70,10 +70,10 @@ module ActionController before_action(options.except(:name, :password, :realm)) do authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password| # This comparison uses & so that it doesn't short circuit and - # uses `variable_size_secure_compare` so that length information + # uses `secure_compare` so that length information # isn't leaked. - ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) & - ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password]) + ActiveSupport::SecurityUtils.secure_compare(name, options[:name]) & + ActiveSupport::SecurityUtils.secure_compare(password, options[:password]) end end end @@ -348,10 +348,7 @@ module ActionController # authenticate_or_request_with_http_token do |token, options| # # Compare the tokens in a time-constant manner, to mitigate # # timing attacks. - # ActiveSupport::SecurityUtils.secure_compare( - # ::Digest::SHA256.hexdigest(token), - # ::Digest::SHA256.hexdigest(TOKEN) - # ) + # ActiveSupport::SecurityUtils.secure_compare(token, TOKEN) # end # end # end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 5051c02a62..13662fc021 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -353,7 +353,7 @@ module ActionController #:nodoc: end def compare_with_real_token(token, session) # :doc: - ActiveSupport::SecurityUtils.secure_compare(token, real_csrf_token(session)) + ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, real_csrf_token(session)) end def valid_per_form_csrf_token?(token, session) # :doc: @@ -364,7 +364,7 @@ module ActionController #:nodoc: request.request_method ) - ActiveSupport::SecurityUtils.secure_compare(token, correct_token) + ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, correct_token) else false end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index fc1e5516f8..8a76e011c1 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,11 @@ +* Changed default behaviour of `ActiveSupport::SecurityUtils.secure_compare`, + to make it not leak length information even for variable length string. + + Renamed old `ActiveSupport::SecurityUtils.secure_compare` to `fixed_length_secure_compare`, + and started raising `ArgumentError` in case of length mismatch of passed strings. + + *Vipul A M* + * Add default option to module and class attribute accessors. mattr_accessor :settings, default: {} diff --git a/activesupport/lib/active_support/security_utils.rb b/activesupport/lib/active_support/security_utils.rb index b655d64449..5f5d4faa60 100644 --- a/activesupport/lib/active_support/security_utils.rb +++ b/activesupport/lib/active_support/security_utils.rb @@ -2,14 +2,12 @@ require "digest" module ActiveSupport module SecurityUtils - # Constant time string comparison. + # Constant time string comparison, for fixed length strings. # # The values compared should be of fixed length, such as strings - # that have already been processed by HMAC. This should not be used - # on variable length plaintext strings because it could leak length info - # via timing attacks. - def secure_compare(a, b) - return false unless a.bytesize == b.bytesize + # that have already been processed by HMAC. Raises in case of length mismatch. + def fixed_length_secure_compare(a, b) + raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize l = a.unpack "C#{a.bytesize}" @@ -17,11 +15,15 @@ module ActiveSupport b.each_byte { |byte| res |= byte ^ l.shift } res == 0 end - module_function :secure_compare + module_function :fixed_length_secure_compare - def variable_size_secure_compare(a, b) # :nodoc: - secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) + # Constant time string comparison, for variable length strings. + # + # The values are first processed by SHA256, so that we don't leak length info + # via timing attacks. + def secure_compare(a, b) + fixed_length_secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) end - module_function :variable_size_secure_compare + module_function :secure_compare end end diff --git a/activesupport/test/security_utils_test.rb b/activesupport/test/security_utils_test.rb index e8f762da22..8d7f7d699a 100644 --- a/activesupport/test/security_utils_test.rb +++ b/activesupport/test/security_utils_test.rb @@ -11,4 +11,15 @@ class SecurityUtilsTest < ActiveSupport::TestCase assert ActiveSupport::SecurityUtils.variable_size_secure_compare("a", "a") assert_not ActiveSupport::SecurityUtils.variable_size_secure_compare("a", "b") end + + def test_fixed_length_secure_compare_should_perform_string_comparison + assert ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "a") + assert !ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "b") + end + + def test_fixed_length_secure_compare_raise_on_length_mismatch + assert_raises(ArgumentError, "string length mismatch.") do + ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "ab") + end + end end -- cgit v1.2.3 From 19afeaf5804b3d6e0738d6a9506235ff50a0497b Mon Sep 17 00:00:00 2001 From: Vasin Dmitriy Date: Wed, 17 May 2017 22:19:52 +0300 Subject: Fix callback in rails ujs --- .../javascripts/rails-ujs/features/remote.coffee | 2 +- .../assets/javascripts/rails-ujs/utils/ajax.coffee | 9 +-- .../test/ujs/public/test/call-remote-callbacks.js | 92 +++++++--------------- 3 files changed, 34 insertions(+), 69 deletions(-) diff --git a/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee b/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee index 852587042c..b3448dabac 100644 --- a/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee @@ -62,7 +62,7 @@ Rails.handleRemote = (e) -> fire(element, 'ajax:send', [xhr]) else fire(element, 'ajax:stopped') - xhr.abort() + return false success: (args...) -> fire(element, 'ajax:success', args) error: (args...) -> fire(element, 'ajax:error', args) complete: (args...) -> fire(element, 'ajax:complete', args) diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee index a653d3af3d..4d7848e162 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee @@ -20,13 +20,12 @@ Rails.ajax = (options) -> else options.error?(response, xhr.statusText, xhr) options.complete?(xhr, xhr.statusText) - # Call beforeSend hook - options.beforeSend?(xhr, options) - # Send the request + + unless options.beforeSend?(xhr, options) + return false + if xhr.readyState is XMLHttpRequest.OPENED xhr.send(options.data) - else - fire(document, 'ajaxStop') # to be compatible with jQuery.ajax prepareOptions = (options) -> options.url = options.url or location.href diff --git a/actionview/test/ujs/public/test/call-remote-callbacks.js b/actionview/test/ujs/public/test/call-remote-callbacks.js index 707e21541d..48763f6301 100644 --- a/actionview/test/ujs/public/test/call-remote-callbacks.js +++ b/actionview/test/ujs/public/test/call-remote-callbacks.js @@ -12,34 +12,20 @@ module('call-remote-callbacks', { $(document).undelegate('form[data-remote]', 'ajax:send') $(document).undelegate('form[data-remote]', 'ajax:complete') $(document).undelegate('form[data-remote]', 'ajax:success') - $(document).unbind('ajaxStop') $(document).unbind('iframe:loading') } }) -function start_after_submit(form) { - form.bindNative('ajax:complete', function() { - ok(true, 'ajax:complete') - start() - }) -} - function submit(fn) { var form = $('form') - start_after_submit(form) if (fn) fn(form) form.triggerNative('submit') -} -function submit_with_button(submit_button) { - var form = $('form') - start_after_submit(form) - - submit_button.triggerNative('click') + setTimeout(function() { start() }, 13) } -asyncTest('modifying form fields with "ajax:before" sends modified data in request', 4, function() { +asyncTest('modifying form fields with "ajax:before" sends modified data in request', 3, function() { $('form[data-remote]') .append($('')) .append($('')) @@ -61,7 +47,7 @@ asyncTest('modifying form fields with "ajax:before" sends modified data in reque }) }) -asyncTest('modifying data("type") with "ajax:before" requests new dataType in request', 2, function() { +asyncTest('modifying data("type") with "ajax:before" requests new dataType in request', 1, function() { $('form[data-remote]').data('type', 'html') .bindNative('ajax:before', function() { this.setAttribute('data-type', 'xml') @@ -74,7 +60,7 @@ asyncTest('modifying data("type") with "ajax:before" requests new dataType in re }) }) -asyncTest('setting data("with-credentials",true) with "ajax:before" uses new setting in request', 2, function() { +asyncTest('setting data("with-credentials",true) with "ajax:before" uses new setting in request', 1, function() { $('form[data-remote]').data('with-credentials', false) .bindNative('ajax:before', function() { this.setAttribute('data-with-credentials', true) @@ -96,14 +82,11 @@ asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function form.unbind('ajax:send').bindNative('ajax:send', function() { ok(false, 'ajax:send should not run') }) - form.unbind('ajax:complete').bindNative('ajax:complete', function() { - ok(false, 'ajax:complete should not run') - }) - form.bindNative('ajax:error', function(e, xhr, status, error) { + form.bindNative('ajax:error', function(e, response, status, xhr) { ok(false, 'ajax:error should not run') }) - $(document).bindNative('ajaxStop', function() { - start() + form.bindNative('ajax:complete', function() { + ok(false, 'ajax:complete should not run') }) }) }) @@ -188,16 +171,13 @@ asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', form.unbind('ajax:send').bindNative('ajax:send', function() { ok(false, 'ajax:send should not run') }) - form.unbind('ajax:complete').bindNative('ajax:complete', function() { + form.bindNative('ajax:complete', function() { ok(false, 'ajax:complete should not run') }) - $(document).bindNative('ajaxStop', function() { - start() - }) }) }) -asyncTest('"ajax:beforeSend", "ajax:send", "ajax:success" and "ajax:complete" are triggered', 9, function() { +asyncTest('"ajax:beforeSend", "ajax:send", "ajax:success" and "ajax:complete" are triggered', 8, function() { submit(function(form) { form.bindNative('ajax:beforeSend', function(e, xhr, settings) { ok(xhr.setRequestHeader, 'first argument to "ajax:beforeSend" should be an XHR object') @@ -218,25 +198,25 @@ asyncTest('"ajax:beforeSend", "ajax:send", "ajax:success" and "ajax:complete" ar }) }) -if(window.phantom !== undefined) { - asyncTest('"ajax:beforeSend", "ajax:send", "ajax:error" and "ajax:complete" are triggered on error', 7, function() { - submit(function(form) { - form.attr('action', '/error') - form.bindNative('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') }) - form.bindNative('ajax:send', function(arg) { ok(true, 'ajax:send') }) - form.bindNative('ajax:error', function(e, xhr, status, error) { - ok(xhr.getResponseHeader, 'first argument to "ajax:error" should be an XHR object') - equal(status, 'error', 'second argument to ajax:error should be a status string') - // Firefox 8 returns "Forbidden " with trailing space - equal($.trim(error), 'Forbidden', 'third argument to ajax:error should be an HTTP status response') - // Opera returns "0" for HTTP code - equal(xhr.status, window.opera ? 0 : 403, 'status code should be 403') - }) +asyncTest('"ajax:beforeSend", "ajax:send", "ajax:error" and "ajax:complete" are triggered on error', 8, function() { + submit(function(form) { + form.attr('action', '/error') + form.bindNative('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') }) + form.bindNative('ajax:send', function(arg) { ok(true, 'ajax:send') }) + form.bindNative('ajax:error', function(e, response, status, xhr) { + equal(response, '', 'first argument to ajax:error should be an HTTP status response') + equal(status, 'Forbidden', 'second argument to ajax:error should be a status string') + ok(xhr.getResponseHeader, 'third argument to "ajax:error" should be an XHR object') + // Opera returns "0" for HTTP code + equal(xhr.status, window.opera ? 0 : 403, 'status code should be 403') + }) + form.bindNative('ajax:complete', function(e, xhr, status) { + ok(xhr.getResponseHeader, 'first argument to "ajax:complete" should be an XHR object') + equal(status, 'Forbidden', 'second argument to ajax:complete should be a status string') }) }) -} +}) -// IF THIS TEST IS FAILING, TRY INCREASING THE TIMEOUT AT THE BOTTOM TO > 100 asyncTest('binding to ajax callbacks via .delegate() triggers handlers properly', 4, function() { $(document) .delegate('form[data-remote]', 'ajax:beforeSend', function() { @@ -245,29 +225,15 @@ asyncTest('binding to ajax callbacks via .delegate() triggers handlers properly' .delegate('form[data-remote]', 'ajax:send', function() { ok(true, 'ajax:send handler is triggered') }) - .delegate('form[data-remote]', 'ajax:complete', function() { - ok(true, 'ajax:complete handler is triggered') - }) .delegate('form[data-remote]', 'ajax:success', function() { ok(true, 'ajax:success handler is triggered') }) - $('form[data-remote]').triggerNative('submit') - - setTimeout(function() { - start() - }, 63) -}) - -asyncTest('binding to ajax:send event to call jquery methods on ajax object', 2, function() { - $('form[data-remote]') - .bindNative('ajax:send', function(e, xhr) { - ok(true, 'event should fire') - equal(typeof(xhr.abort), 'function', 'event should pass jqXHR object') - xhr.abort() + .delegate('form[data-remote]', 'ajax:complete', function() { + ok(true, 'ajax:complete handler is triggered') }) - .triggerNative('submit') + $('form[data-remote]').triggerNative('submit') - setTimeout(function() { start() }, 35) + setTimeout(function() { start() }, 13) }) })() -- cgit v1.2.3 From ff4b18358d06c6ce5f36bf171941b00ed321ab0c Mon Sep 17 00:00:00 2001 From: Marc Rendl Ignacio Date: Fri, 7 Jul 2017 15:34:35 +0800 Subject: Adds descriptions to rails-ujs methods [ci skip] --- .../app/assets/javascripts/rails-ujs/utils/dom.coffee | 7 +++++++ .../assets/javascripts/rails-ujs/utils/event.coffee | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee index 6bef618147..3d3c5bb330 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/dom.coffee @@ -5,6 +5,13 @@ m = Element.prototype.matches or Element.prototype.oMatchesSelector or Element.prototype.webkitMatchesSelector +# Checks if the given native dom element matches the selector +# element:: +# native DOM element +# selector:: +# css selector string or +# a javascript object with `selector` and `exclude` properties +# Examples: "form", { selector: "form", exclude: "form[data-remote='true']"} Rails.matches = (element, selector) -> if selector.exclude? m.call(element, selector.selector) and not m.call(element, selector.exclude) diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee index 8d3ff007ea..a2135c9851 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/event.coffee @@ -14,6 +14,13 @@ if typeof CustomEvent isnt 'function' CustomEvent.prototype = window.Event.prototype # Triggers a custom event on an element and returns false if the event result is false +# obj:: +# a native DOM element +# name:: +# string that corrspends to the event you want to trigger +# e.g. 'click', 'submit' +# data:: +# data you want to pass when you dispatch an event fire = Rails.fire = (obj, name, data) -> event = new CustomEvent( name, @@ -31,6 +38,17 @@ Rails.stopEverything = (e) -> e.stopPropagation() e.stopImmediatePropagation() +# Delegates events +# to a specified parent `element`, which fires event `handler` +# for the specified `selector` when an event of `eventType` is triggered +# element:: +# parent element that will listen for events e.g. document +# selector:: +# css selector; or an object that has `selector` and `exclude` properties (see: Rails.matches) +# eventType:: +# string representing the event e.g. 'submit', 'click' +# handler:: +# the event handler to be called Rails.delegate = (element, selector, eventType, handler) -> element.addEventListener eventType, (e) -> target = e.target -- cgit v1.2.3 From 134ffd290adfdbfb6c291115f5d7fb297314002c Mon Sep 17 00:00:00 2001 From: Mark James Date: Thu, 13 Jul 2017 14:15:52 +1000 Subject: Change the "International Date Line West" TZInfo timezone from "Pacific/Midway" (-11) to "Etc/GMT+12" (-12). --- activesupport/lib/active_support/values/time_zone.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index c871f11422..a5a88617bd 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -30,7 +30,7 @@ module ActiveSupport class TimeZone # Keys are Rails TimeZone names, values are TZInfo identifiers. MAPPING = { - "International Date Line West" => "Pacific/Midway", + "International Date Line West" => "Etc/GMT+12", "Midway Island" => "Pacific/Midway", "American Samoa" => "Pacific/Pago_Pago", "Hawaii" => "Pacific/Honolulu", -- cgit v1.2.3 From a41c55c0a36f3a0cedc2a72ae2b3d6a5a64d52ce Mon Sep 17 00:00:00 2001 From: Kir Shatrov Date: Sun, 6 Aug 2017 21:40:55 +0300 Subject: Document public hooks in AS::Reloader [ci skip] --- activesupport/lib/active_support/reloader.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/activesupport/lib/active_support/reloader.rb b/activesupport/lib/active_support/reloader.rb index 44062e3491..1e3141dc2a 100644 --- a/activesupport/lib/active_support/reloader.rb +++ b/activesupport/lib/active_support/reloader.rb @@ -28,14 +28,17 @@ module ActiveSupport define_callbacks :class_unload + # Registers a callback that will run once at application startup and every time the code is reloaded. def self.to_prepare(*args, &block) set_callback(:prepare, *args, &block) end + # Registers a callback that will run immediately before the classes are unloaded. def self.before_class_unload(*args, &block) set_callback(:class_unload, *args, &block) end + # Registers a callback that will run immediately after the classes are unloaded. def self.after_class_unload(*args, &block) set_callback(:class_unload, :after, *args, &block) end -- cgit v1.2.3 From c342d58446defad94f26c067b19003c9941f2470 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Tue, 29 Aug 2017 20:12:10 -0400 Subject: activerecord: Remove a redundant mutation tracker The extra mutation tracker was needed in Rails 5.1 to preserve the old behaviour of `changes`, but now there is no difference between `changes` and `changes_to_save`, so `@mutation_tracker` can be removed. --- .../lib/active_record/attribute_methods/dirty.rb | 32 ++++++---------------- activerecord/lib/active_record/persistence.rb | 2 +- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 4f957ac3ca..06598439d8 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -34,7 +34,7 @@ module ActiveRecord def reload(*) super.tap do @mutations_before_last_save = nil - clear_mutation_trackers + @mutations_from_database = nil @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new end end @@ -44,22 +44,21 @@ module ActiveRecord @attributes = self.class._default_attributes.map do |attr| attr.with_value_from_user(@attributes.fetch_value(attr.name)) end - clear_mutation_trackers + @mutations_from_database = nil end def changes_applied # :nodoc: - @mutations_before_last_save = mutation_tracker - @mutations_from_database = AttributeMutationTracker.new(@attributes) + @mutations_before_last_save = mutations_from_database @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new forget_attribute_assignments - clear_mutation_trackers + @mutations_from_database = nil end def clear_changes_information # :nodoc: @mutations_before_last_save = nil @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new forget_attribute_assignments - clear_mutation_trackers + @mutations_from_database = nil end def clear_attribute_changes(attr_names) # :nodoc: @@ -75,7 +74,7 @@ module ActiveRecord if defined?(@cached_changed_attributes) @cached_changed_attributes else - super.reverse_merge(mutation_tracker.changed_values).freeze + super.reverse_merge(mutations_from_database.changed_values).freeze end end @@ -90,7 +89,7 @@ module ActiveRecord end def attribute_changed_in_place?(attr_name) # :nodoc: - mutation_tracker.changed_in_place?(attr_name) + mutations_from_database.changed_in_place?(attr_name) end # Did this attribute change when we last saved? This method can be invoked @@ -183,26 +182,18 @@ module ActiveRecord result end - def mutation_tracker - unless defined?(@mutation_tracker) - @mutation_tracker = nil - end - @mutation_tracker ||= AttributeMutationTracker.new(@attributes) - end - def mutations_from_database unless defined?(@mutations_from_database) @mutations_from_database = nil end - @mutations_from_database ||= mutation_tracker + @mutations_from_database ||= AttributeMutationTracker.new(@attributes) end def changes_include?(attr_name) - super || mutation_tracker.changed?(attr_name) + super || mutations_from_database.changed?(attr_name) end def clear_attribute_change(attr_name) - mutation_tracker.forget_change(attr_name) mutations_from_database.forget_change(attr_name) end @@ -227,11 +218,6 @@ module ActiveRecord @attributes = @attributes.map(&:forgetting_assignment) end - def clear_mutation_trackers - @mutation_tracker = nil - @mutations_from_database = nil - end - def mutations_before_last_save @mutations_before_last_save ||= NullMutationTracker.instance end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index fbbf9082cc..b28f6e96a9 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -228,7 +228,7 @@ module ActiveRecord def becomes(klass) became = klass.new became.instance_variable_set("@attributes", @attributes) - became.instance_variable_set("@mutation_tracker", @mutation_tracker) if defined?(@mutation_tracker) + became.instance_variable_set("@mutations_from_database", @mutations_from_database) if defined?(@mutations_from_database) became.instance_variable_set("@changed_attributes", attributes_changed_by_setter) became.instance_variable_set("@new_record", new_record?) became.instance_variable_set("@destroyed", destroyed?) -- cgit v1.2.3 From 913742af0bbcace4e39c3f516d599bfc0ae5ea82 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sun, 3 Sep 2017 00:23:01 +0900 Subject: Add :nodoc: to activerecord [ci skip] --- .../connection_adapters/abstract_mysql_adapter.rb | 2 +- activerecord/lib/active_record/migration/command_recorder.rb | 2 +- activerecord/lib/active_record/scoping.rb | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) 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 7cd086084a..fee362f7b0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -44,7 +44,7 @@ module ActiveRecord json: { name: "json" }, } - class StatementPool < ConnectionAdapters::StatementPool + class StatementPool < ConnectionAdapters::StatementPool # :nodoc: private def dealloc(stmt) stmt[:stmt].close end diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index a3a5e0fa16..72ce9c3655 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -110,7 +110,7 @@ module ActiveRecord private - module StraightReversions + module StraightReversions # :nodoc: private { transaction: :transaction, execute_block: :execute_block, diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb index da585a9562..01ac56570a 100644 --- a/activerecord/lib/active_record/scoping.rb +++ b/activerecord/lib/active_record/scoping.rb @@ -11,23 +11,23 @@ module ActiveRecord include Named end - module ClassMethods - def current_scope(skip_inherited_scope = false) # :nodoc: + module ClassMethods # :nodoc: + def current_scope(skip_inherited_scope = false) ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope) end - def current_scope=(scope) #:nodoc: + def current_scope=(scope) ScopeRegistry.set_value_for(:current_scope, self, scope) end # Collects attributes from scopes that should be applied when creating # an AR instance for the particular class this is called on. - def scope_attributes # :nodoc: + def scope_attributes all.scope_for_create end # Are there attributes associated with this scope? - def scope_attributes? # :nodoc: + def scope_attributes? current_scope end end -- cgit v1.2.3 From a5d80d4a1c4a41ec92985e34cac23bac0509e7fb Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 3 Sep 2017 16:49:38 +0900 Subject: Does not include disabled element in params In the case of remote, it should be the same behavior as submitting HTML form. Fixes #30444 --- actionview/app/assets/javascripts/rails-ujs/utils/form.coffee | 2 +- actionview/test/ujs/public/test/data-remote.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee index 5fa337b518..736cab08db 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee @@ -10,7 +10,7 @@ Rails.serializeElement = (element, additionalParam) -> params = [] inputs.forEach (input) -> - return unless input.name + return if !input.name || input.disabled if matches(input, 'select') toArray(input.options).forEach (option) -> params.push(name: input.name, value: option.value) if option.selected diff --git a/actionview/test/ujs/public/test/data-remote.js b/actionview/test/ujs/public/test/data-remote.js index 9bbefc18f2..cbbd4e6c92 100644 --- a/actionview/test/ujs/public/test/data-remote.js +++ b/actionview/test/ujs/public/test/data-remote.js @@ -191,9 +191,10 @@ asyncTest('submitting form with data-remote attribute should include inputs in a .triggerNative('submit') }) -asyncTest('submitting form with data-remote attribute submits input with matching [form] attribute', 5, function() { +asyncTest('submitting form with data-remote attribute submits input with matching [form] attribute', 6, function() { $('#qunit-fixture') .append($('')) + .append($('')) $('form[data-remote]') .bindNative('ajax:success', function(e, data, status, xhr) { @@ -201,6 +202,7 @@ asyncTest('submitting form with data-remote attribute submits input with matchin App.assertRequestPath(data, '/echo') equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value') equal(data.params.user_data, 'value1', 'ajax arguments should have key user_data with right value') + equal(data.params.user_email, undefined, 'ajax arguments should not have disabled field') App.assertPostRequest(data) }) .bindNative('ajax:complete', function() { start() }) -- cgit v1.2.3 From 8a331566bfe05ccc85f25805693aa892cec449a5 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 15 Sep 2017 14:31:02 +0900 Subject: Add tests for credentials command --- railties/test/commands/credentials_test.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb index fe52c306d2..743fb5f788 100644 --- a/railties/test/commands/credentials_test.rb +++ b/railties/test/commands/credentials_test.rb @@ -12,6 +12,21 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase teardown { teardown_app } + test "edit without editor gives hint" do + assert_match "No $EDITOR to open credentials in", run_edit_command(editor: "") + end + + test "edit credentials" do + # Run twice to ensure credentials can be reread after first edit pass. + 2.times do + assert_match(/access_key_id: 123/, run_edit_command) + end + end + + test "show credentials" do + assert_match(/access_key_id: 123/, run_show_command) + end + test "edit command does not add master key to gitignore when already exist" do run_edit_command @@ -27,4 +42,8 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase rails "credentials:edit" end end + + def run_show_command + rails "credentials:show" + end end -- cgit v1.2.3 From bad8c9ccc91211e06e1f92b21d5eed15b2f7b2c0 Mon Sep 17 00:00:00 2001 From: kami-zh Date: Sat, 16 Sep 2017 03:03:20 +0900 Subject: Install JavaScript dependencies on update --- railties/lib/rails/generators/rails/app/templates/bin/update.tt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt index d744bec32f..70cc71d83b 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt +++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt @@ -15,6 +15,11 @@ chdir APP_ROOT do puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') +<% unless options.skip_yarn? -%> + + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') +<% end -%> <% unless options.skip_active_record? -%> puts "\n== Updating database ==" -- cgit v1.2.3 From 3bf95f95130ec00194c7a7d5dd9d6c69f702214c Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 16 Sep 2017 14:51:26 +0900 Subject: Don't expose Active Storage routes These routes are only used internally in Active Storage, and it seems that there is no need for the user to directly use them. Therefore, I think that routes should not be exposed to users. --- activestorage/config/routes.rb | 10 ++++----- railties/test/application/rake_test.rb | 41 +++++++++++----------------------- 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/activestorage/config/routes.rb b/activestorage/config/routes.rb index 168788475c..c3194887be 100644 --- a/activestorage/config/routes.rb +++ b/activestorage/config/routes.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true Rails.application.routes.draw do - get "/rails/active_storage/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob + get "/rails/active_storage/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob, internal: true direct :rails_blob do |blob| route_for(:rails_service_blob, blob.signed_id, blob.filename) @@ -11,7 +11,7 @@ Rails.application.routes.draw do resolve("ActiveStorage::Attachment") { |attachment| route_for(:rails_blob, attachment.blob) } - get "/rails/active_storage/variants/:signed_blob_id/:variation_key/*filename" => "active_storage/variants#show", as: :rails_blob_variation + get "/rails/active_storage/variants/:signed_blob_id/:variation_key/*filename" => "active_storage/variants#show", as: :rails_blob_variation, internal: true direct :rails_variant do |variant| signed_blob_id = variant.blob.signed_id @@ -24,7 +24,7 @@ Rails.application.routes.draw do resolve("ActiveStorage::Variant") { |variant| route_for(:rails_variant, variant) } - get "/rails/active_storage/disk/:encoded_key/*filename" => "active_storage/disk#show", as: :rails_disk_service - put "/rails/active_storage/disk/:encoded_token" => "active_storage/disk#update", as: :update_rails_disk_service - post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads + get "/rails/active_storage/disk/:encoded_key/*filename" => "active_storage/disk#show", as: :rails_disk_service, internal: true + put "/rails/active_storage/disk/:encoded_token" => "active_storage/disk#update", as: :update_rails_disk_service, internal: true + post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads, internal: true end diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index f9b14f98cb..76bc0ce1d7 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -133,13 +133,8 @@ module ApplicationTests output = rails("routes") assert_equal <<-MESSAGE.strip_heredoc, output - Prefix Verb URI Pattern Controller#Action - cart GET /cart(.:format) cart#show - rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show - rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show - rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show - update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update - rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + Prefix Verb URI Pattern Controller#Action + cart GET /cart(.:format) cart#show MESSAGE end @@ -174,18 +169,14 @@ module ApplicationTests output = rails("routes", "-g", "show", allow_failure: true) assert_equal <<-MESSAGE.strip_heredoc, output - Prefix Verb URI Pattern Controller#Action - cart GET /cart(.:format) cart#show - rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show - rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show - rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show + Prefix Verb URI Pattern Controller#Action + cart GET /cart(.:format) cart#show MESSAGE output = rails("routes", "-g", "POST") assert_equal <<-MESSAGE.strip_heredoc, output - Prefix Verb URI Pattern Controller#Action - POST /cart(.:format) cart#create - rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + Prefix Verb URI Pattern Controller#Action + POST /cart(.:format) cart#create MESSAGE output = rails("routes", "-g", "basketballs") @@ -242,12 +233,11 @@ module ApplicationTests RUBY assert_equal <<-MESSAGE.strip_heredoc, rails("routes") - Prefix Verb URI Pattern Controller#Action - rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show - rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show - rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show - update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update - rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + You don't have any routes defined! + + Please add some routes in config/routes.rb. + + For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html. MESSAGE end @@ -261,13 +251,8 @@ module ApplicationTests output = Dir.chdir(app_path) { `bin/rake --rakefile Rakefile routes` } assert_equal <<-MESSAGE.strip_heredoc, output - Prefix Verb URI Pattern Controller#Action - cart GET /cart(.:format) cart#show - rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show - rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show - rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show - update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update - rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + Prefix Verb URI Pattern Controller#Action + cart GET /cart(.:format) cart#show MESSAGE end -- cgit v1.2.3 From c330cc85a7e9fa11be160e810c77a3e1507fb25d Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sat, 16 Sep 2017 07:57:50 +0900 Subject: Update Form Helpers guide to fix example codes [ci skip] * It looks that example codes are not based on actual output. So I've fixed it. * Specifically: * There are no lines about utf-8 and authenticity_token. * The submit button doesn't have data-disabled-with attribute. * Each attribute order of html element is different from actual ones. --- guides/source/form_helpers.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index f46f1648b3..4ce67df93a 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -274,10 +274,12 @@ There are a few things to note here: The resulting HTML is: ```html -
- - - + + + + + +
``` @@ -299,9 +301,11 @@ You can create a similar binding without actually creating `
` tags with th which produces the following output: ```html - - - + + + + +
``` -- cgit v1.2.3 From a52e2bed8634531272018a4ae7ba389e1feebdf5 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sat, 16 Sep 2017 08:33:57 +0900 Subject: Update Layouts and Rendering in Rails [ci skip] * The example code on this page are similar to ones generated by scaffold generator, but some points are different. * Of course, it is no reason to be as same as scaffolding codes. But this is the guide for beginners, I thought it's better to be almost same as scaffolding codes. --- guides/source/layouts_and_rendering.md | 36 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 76b325d0bf..fe2477f2ae 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -71,23 +71,25 @@ If we want to display the properties of all the books in our view, we can do so

Listing Books

- - - - - - - - -<% @books.each do |book| %> - - - - - - - -<% end %> + + + + + + + + + + <% @books.each do |book| %> + + + + + + + + <% end %> +
TitleSummary
<%= book.title %><%= book.content %><%= link_to "Show", book %><%= link_to "Edit", edit_book_path(book) %><%= link_to "Remove", book, method: :delete, data: { confirm: "Are you sure?" } %>
TitleContent
<%= book.title %><%= book.content %><%= link_to "Show", book %><%= link_to "Edit", edit_book_path(book) %><%= link_to "Destroy", book, method: :delete, data: { confirm: "Are you sure?" } %>

-- cgit v1.2.3 From 9e4827a8ae40de50503c530b8cfd0a0789e27956 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sun, 17 Sep 2017 02:20:02 +0300 Subject: Deprecate `Module#reachable?` method --- activesupport/CHANGELOG.md | 4 ++++ .../active_support/core_ext/module/reachable.rb | 1 + .../test/core_ext/module/reachable_test.rb | 28 ++++++++++++++-------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index f158d5357d..56013c5f95 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,7 @@ +* Deprecate `Module#reachable?` method. + + *bogdanvlviv* + * Add `config/credentials.yml.enc` to store production app secrets. Allows saving any authentication credentials for third party services diff --git a/activesupport/lib/active_support/core_ext/module/reachable.rb b/activesupport/lib/active_support/core_ext/module/reachable.rb index 91b230b46c..790a3cc561 100644 --- a/activesupport/lib/active_support/core_ext/module/reachable.rb +++ b/activesupport/lib/active_support/core_ext/module/reachable.rb @@ -7,4 +7,5 @@ class Module def reachable? #:nodoc: !anonymous? && name.safe_constantize.equal?(self) end + deprecate :reachable? end diff --git a/activesupport/test/core_ext/module/reachable_test.rb b/activesupport/test/core_ext/module/reachable_test.rb index a69fc6839e..097a72fa5b 100644 --- a/activesupport/test/core_ext/module/reachable_test.rb +++ b/activesupport/test/core_ext/module/reachable_test.rb @@ -5,13 +5,17 @@ require "active_support/core_ext/module/reachable" class AnonymousTest < ActiveSupport::TestCase test "an anonymous class or module is not reachable" do - assert !Module.new.reachable? - assert !Class.new.reachable? + assert_deprecated do + assert !Module.new.reachable? + assert !Class.new.reachable? + end end test "ordinary named classes or modules are reachable" do - assert Kernel.reachable? - assert Object.reachable? + assert_deprecated do + assert Kernel.reachable? + assert Object.reachable? + end end test "a named class or module whose constant has gone is not reachable" do @@ -21,8 +25,10 @@ class AnonymousTest < ActiveSupport::TestCase self.class.send(:remove_const, :C) self.class.send(:remove_const, :M) - assert !c.reachable? - assert !m.reachable? + assert_deprecated do + assert !c.reachable? + assert !m.reachable? + end end test "a named class or module whose constants store different objects are not reachable" do @@ -35,9 +41,11 @@ class AnonymousTest < ActiveSupport::TestCase eval "class C; end" eval "module M; end" - assert C.reachable? - assert M.reachable? - assert !c.reachable? - assert !m.reachable? + assert_deprecated do + assert C.reachable? + assert M.reachable? + assert !c.reachable? + assert !m.reachable? + end end end -- cgit v1.2.3 From c7e33c745be122eca41083a531765f7694a2e689 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sun, 17 Sep 2017 02:28:55 +0300 Subject: Remove redundant require_relative "module/anonymous" and "module/reachable" --- activesupport/lib/active_support/core_ext/class/subclasses.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb index 4c910feb44..75e65337b7 100644 --- a/activesupport/lib/active_support/core_ext/class/subclasses.rb +++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require_relative "../module/anonymous" -require_relative "../module/reachable" - class Class begin # Test if this Ruby supports each_object against singleton_class -- cgit v1.2.3 From 275065355c833b769daf898bf7940c0ff98b6a7c Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sun, 17 Sep 2017 02:32:32 +0300 Subject: Remove mentions about `Module#reachable?` in "Active Support Core Extensions" guide --- guides/source/active_support_core_extensions.md | 39 ------------------------- 1 file changed, 39 deletions(-) diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 1438245f9c..ae573cc77c 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -674,44 +674,6 @@ M.parents # => [X::Y, X, Object] NOTE: Defined in `active_support/core_ext/module/introspection.rb`. -### Reachable - -A named module is reachable if it is stored in its corresponding constant. It means you can reach the module object via the constant. - -That is what ordinarily happens, if a module is called "M", the `M` constant exists and holds it: - -```ruby -module M -end - -M.reachable? # => true -``` - -But since constants and modules are indeed kind of decoupled, module objects can become unreachable: - -```ruby -module M -end - -orphan = Object.send(:remove_const, :M) - -# The module object is orphan now but it still has a name. -orphan.name # => "M" - -# You cannot reach it via the constant M because it does not even exist. -orphan.reachable? # => false - -# Let's define a module called "M" again. -module M -end - -# The constant M exists now again, and it stores a module -# object called "M", but it is a new instance. -orphan.reachable? # => false -``` - -NOTE: Defined in `active_support/core_ext/module/reachable.rb`. - ### Anonymous A module may or may not have a name: @@ -745,7 +707,6 @@ end m = Object.send(:remove_const, :M) -m.reachable? # => false m.anonymous? # => false ``` -- cgit v1.2.3 From e0fa2ce9614d4bc28ddc484d5b4fe2f54b402e8a Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 17 Sep 2017 09:29:30 +0900 Subject: Remove frozen_string_literal magic comment from templates Follow up of #30348 --- .../rails/generators/test_unit/controller/templates/functional_test.rb | 2 -- .../rails/generators/test_unit/generator/templates/generator_test.rb | 2 -- .../generators/test_unit/integration/templates/integration_test.rb | 2 -- .../lib/rails/generators/test_unit/mailer/templates/functional_test.rb | 2 -- railties/lib/rails/generators/test_unit/mailer/templates/preview.rb | 2 -- railties/lib/rails/generators/test_unit/model/templates/unit_test.rb | 2 -- railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb | 2 -- .../generators/test_unit/scaffold/templates/api_functional_test.rb | 2 -- .../rails/generators/test_unit/scaffold/templates/functional_test.rb | 2 -- .../lib/rails/generators/test_unit/scaffold/templates/system_test.rb | 2 -- .../test_unit/system/templates/application_system_test_case.rb | 2 -- railties/lib/rails/generators/test_unit/system/templates/system_test.rb | 2 -- 12 files changed, 24 deletions(-) diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb index 4efa977a89..ff41fef9e9 100644 --- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'test_helper' <% module_namespacing do -%> diff --git a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb index e6fb6c5ff4..a7f1fc4fba 100644 --- a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb +++ b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'test_helper' require '<%= generator_path %>' diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb index 65708b6c3b..118e0f1271 100644 --- a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb +++ b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'test_helper' <% module_namespacing do -%> diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb index 1ec3a2f360..a2f2d30de5 100644 --- a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'test_helper' <% module_namespacing do -%> diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb index 9876210b6c..b063cbc47b 100644 --- a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb +++ b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - <% module_namespacing do -%> # Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %>_mailer class <%= class_name %>MailerPreview < ActionMailer::Preview diff --git a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb index 5f1ffeb33b..c9bc7d5b90 100644 --- a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb +++ b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'test_helper' <% module_namespacing do -%> diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb index 2147b09568..30a861f09d 100644 --- a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +++ b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb @@ -1,4 +1,2 @@ -# frozen_string_literal: true - require 'active_support/testing/autorun' require 'active_support' diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb index 2ef93b8aea..f21861d8e6 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'test_helper' <% module_namespacing do -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb index bcf9392bd1..195d60be20 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'test_helper' <% module_namespacing do -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb index ba8bdc192e..f83f5a5c62 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "application_system_test_case" <% module_namespacing do -%> diff --git a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb index c05709aff8..d19212abd5 100644 --- a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb +++ b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "test_helper" class ApplicationSystemTestCase < ActionDispatch::SystemTestCase diff --git a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb index cfac061cd1..b5ce2ba5c8 100644 --- a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb +++ b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "application_system_test_case" class <%= class_name.pluralize %>Test < ApplicationSystemTestCase -- cgit v1.2.3 From 194a93385b08be857172b8eb6287d335c5adf1ea Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sun, 17 Sep 2017 17:40:20 +0900 Subject: Add local option to Message form [ci skip] * MessagesController redirects to `GET /message/:id`. * It looks it don't expect XHR request. * `form_with` behaves for XHR by default. * I've added `local: true` option to `form_with`. --- activestorage/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/README.md b/activestorage/README.md index 17d98978bb..5b73740f70 100644 --- a/activestorage/README.md +++ b/activestorage/README.md @@ -63,7 +63,7 @@ end ``` ```erb -<%= form_with model: @message do |form| %> +<%= form_with model: @message, local: true do |form| %> <%= form.text_field :title, placeholder: "Title" %>
<%= form.text_area :content %>

-- cgit v1.2.3 From 7d14bda3a7c74ec0262fa021e641a4235f5405d5 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sun, 17 Sep 2017 10:06:47 +0900 Subject: Fix file missing in activestorage's example code [ci skip] * File.open("~/face.jpg") raise error: `Errno::ENOENT: No such file or directory @ rb_sysopen - ~/face.jpg` --- activestorage/README.md | 2 +- activestorage/lib/active_storage/attached/many.rb | 2 +- activestorage/lib/active_storage/attached/one.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activestorage/README.md b/activestorage/README.md index 17d98978bb..f4aae4fc00 100644 --- a/activestorage/README.md +++ b/activestorage/README.md @@ -24,7 +24,7 @@ class User < ApplicationRecord end # Attach an avatar to the user. -user.avatar.attach(io: File.open("~/face.jpg"), filename: "avatar.jpg", content_type: "image/jpg") +user.avatar.attach(io: File.open("/path/to/face.jpg"), filename: "face.jpg", content_type: "image/jpg") # Does the user have an avatar? user.avatar.attached? # => true diff --git a/activestorage/lib/active_storage/attached/many.rb b/activestorage/lib/active_storage/attached/many.rb index 59b7d7d559..1e0657c33c 100644 --- a/activestorage/lib/active_storage/attached/many.rb +++ b/activestorage/lib/active_storage/attached/many.rb @@ -17,7 +17,7 @@ module ActiveStorage # # document.images.attach(params[:images]) # Array of ActionDispatch::Http::UploadedFile objects # document.images.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload - # document.images.attach(io: File.open("~/racecar.jpg"), filename: "racecar.jpg", content_type: "image/jpg") + # document.images.attach(io: File.open("/path/to/racecar.jpg"), filename: "racecar.jpg", content_type: "image/jpg") # document.images.attach([ first_blob, second_blob ]) def attach(*attachables) attachables.flatten.collect do |attachable| diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb index ac90f32d95..c66be08f58 100644 --- a/activestorage/lib/active_storage/attached/one.rb +++ b/activestorage/lib/active_storage/attached/one.rb @@ -18,7 +18,7 @@ module ActiveStorage # # person.avatar.attach(params[:avatar]) # ActionDispatch::Http::UploadedFile object # person.avatar.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload - # person.avatar.attach(io: File.open("~/face.jpg"), filename: "face.jpg", content_type: "image/jpg") + # person.avatar.attach(io: File.open("/path/to/face.jpg"), filename: "face.jpg", content_type: "image/jpg") # person.avatar.attach(avatar_blob) # ActiveStorage::Blob object def attach(attachable) if attached? && dependent == :purge_later -- cgit v1.2.3 From e484a2d1090079ed7b4a3a398ae0fee5a12e648b Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Sun, 17 Sep 2017 15:40:11 +0300 Subject: Update plugins.md [ci skip] --- guides/source/plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/plugins.md b/guides/source/plugins.md index 0f0cde7634..b3a7f544f5 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -237,7 +237,7 @@ Finished in 0.004812s, 831.2949 runs/s, 415.6475 assertions/s. This tells us that we don't have the necessary models (Hickwall and Wickwall) that we are trying to test. We can easily generate these models in our "dummy" Rails application by running the following commands from the -test/dummy directory: +`test/dummy` directory: ```bash $ cd test/dummy -- cgit v1.2.3 From 7c4dad364cd9e02cf3b8a14fcdbbb3b59428da61 Mon Sep 17 00:00:00 2001 From: dixpac Date: Sun, 17 Sep 2017 16:50:32 +0200 Subject: Fix docs describing rollback [ci skip] * `rails db:migrate STEP = 2` will not rollback the migrations, instead `rails db:rollback STEP = 2` will do the rollback. * Also, rewritten `rails db:migrate VERSION` => `rails db:rollback VERSION` for consistency. --- activerecord/lib/active_record/migration.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 52ca4671c2..8845e26ab7 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -354,9 +354,9 @@ module ActiveRecord # to match the structure of your database. # # To roll the database back to a previous migration version, use - # rails db:migrate VERSION=X where X is the version to which + # rails db:rollback VERSION=X where X is the version to which # you wish to downgrade. Alternatively, you can also use the STEP option if you - # wish to rollback last few migrations. rails db:migrate STEP=2 will rollback + # wish to rollback last few migrations. rails db:rollback STEP=2 will rollback # the latest two migrations. # # If any of the migrations throw an ActiveRecord::IrreversibleMigration exception, -- cgit v1.2.3 From 1df25e6f800d4202e5ee884d2819599e6e35e806 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sun, 17 Sep 2017 23:53:41 +0900 Subject: Add `mini_magick` to default `Gemfile` as comment * If we want to transform image on ActiveStorage, we should bundle `mini_magick`. * I've added comment block to default `Gemfile` to be easier to install this. --- railties/CHANGELOG.md | 4 ++++ railties/lib/rails/generators/rails/app/templates/Gemfile | 3 +++ 2 files changed, 7 insertions(+) diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 5057059898..ff440b7939 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add `mini_magick` to default `Gemfile` as comment. + + *Yoshiyuki Hirano* + * Derive `secret_key_base` from the app name in development and test environments. Spares away needless secret configs. diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index 7b7bebc957..770247d8ca 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -21,6 +21,9 @@ ruby <%= "'#{RUBY_VERSION}'" -%> # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' +# Use ActiveStorage variant +# gem 'mini_magick', '~> 4.8' + # Use Capistrano for deployment # gem 'capistrano-rails', group: :development -- cgit v1.2.3 From 846990b4881adc5612c3f818a3e1cc5695b3906d Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sun, 17 Sep 2017 20:39:46 +0300 Subject: Add missing round bracket in "Ruby on Rails 5.1 Release Notes" guide [ci skip] --- guides/source/5_1_release_notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/5_1_release_notes.md b/guides/source/5_1_release_notes.md index fa92b9e5f8..80c9da6446 100644 --- a/guides/source/5_1_release_notes.md +++ b/guides/source/5_1_release_notes.md @@ -602,7 +602,7 @@ Please refer to the [Changelog][active-support] for detailed changes. ([Pull Request](https://github.com/rails/rails/pull/28157)) * Deprecated passing string to `:if` and `:unless` conditional options on `set_callback` and `skip_callback`. - ([Commit](https://github.com/rails/rails/commit/0952552) + ([Commit](https://github.com/rails/rails/commit/0952552)) ### Notable changes -- cgit v1.2.3 From 7f8f66e93136ef7ff602b6cdb1f6a7acfaafa486 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 06:17:09 +0900 Subject: Remove useless condition in `reset_association` `through_scope` is not empty scope if `options[:source_type]` is given. --- .../lib/active_record/associations/preloader/through_association.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index 8aac00d910..7b37ec2f41 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -68,8 +68,7 @@ module ActiveRecord end def reset_association(owners, association_name, through_scope) - should_reset = (through_scope != through_reflection.klass.unscoped) || - (options[:source_type] && through_reflection.collection?) + should_reset = through_scope != through_reflection.klass.unscoped # Don't cache the association - we would only be caching a subset if should_reset -- cgit v1.2.3 From 45588fef7eef095d33beaf192b0b6a8c9c8af348 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 06:42:47 +0900 Subject: Return `through_scope` only if the scope is not empty scope Related 2b5f5cdd7c1d95716de6a206b6d09ccbb006dc17. If `through_scope` is empty scope, it is unnecessary to merge it. And also, comparing relations is a little expensive (will cause `build_arel`). It is enough to use `empty_scope?` to determine whether empty scope. --- .../lib/active_record/associations/preloader/through_association.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index 7b37ec2f41..26bcdcbcc4 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -67,9 +67,7 @@ module ActiveRecord id_map end - def reset_association(owners, association_name, through_scope) - should_reset = through_scope != through_reflection.klass.unscoped - + def reset_association(owners, association_name, should_reset) # Don't cache the association - we would only be caching a subset if should_reset owners.each { |owner| @@ -112,7 +110,7 @@ module ActiveRecord end end - scope + scope unless scope.empty_scope? end end end -- cgit v1.2.3 From 5daf89f838abfd0c56d4a09bd352c987bc64ff9a Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 07:07:23 +0900 Subject: Don't pass `reflection_scope` to `preload_scope` if `reflection.scope` isn't given Related 2b5f5cdd7c1d95716de6a206b6d09ccbb006dc17. If `reflection.scope` isn't given, `reflection_scope` is always empty scope. It is unnecessary to merge it. --- .../lib/active_record/associations/preloader/through_association.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index 26bcdcbcc4..53e9c14823 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -28,6 +28,8 @@ module ActiveRecord middle_records = through_records.flat_map(&:last) + reflection_scope = reflection_scope() if reflection.scope + preloaders = preloader.preload(middle_records, source_reflection.name, reflection_scope) @@ -49,7 +51,7 @@ module ActiveRecord }.compact # Respect the order on `reflection_scope` if it exists, else use the natural order. - if reflection_scope.values[:order].present? + if reflection_scope && !reflection_scope.order_values.empty? @id_map ||= id_to_index_map @preloaded_records rhs_records.sort_by { |rhs| @id_map[rhs] } else -- cgit v1.2.3 From 4bb4824ae711b7b550e8e33b428110575adaefac Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 07:45:34 +0900 Subject: Early return if `records.empty?` in `Preloader#preload` --- activerecord/lib/active_record/associations/preloader.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index 62caf02a2c..e1754d4a19 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -91,13 +91,13 @@ module ActiveRecord # { author: :avatar } # [ :books, { author: :avatar } ] def preload(records, associations, preload_scope = nil) - records = Array.wrap(records).compact.uniq - associations = Array.wrap(associations) + records = records.compact if records.empty? [] else - associations.flat_map { |association| + records.uniq! + Array.wrap(associations).flat_map { |association| preloaders_on association, records, preload_scope } end -- cgit v1.2.3 From 9ac7dd47c5e847f7dbfb8d527ee2b917fa9fcd38 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 11 Sep 2017 09:41:08 +0900 Subject: Place class level `update`, `destroy`, and `delete` in `Persistence::ClassMethods` The docs are obviously for class level `update`, `destroy`, and `delete`. It should be placed in `Persistence::ClassMethods` rather than `Relation`. And also, these methods are not dependent on relation. So it is not needed to delegate to `all` (plus, `klass.find` is faster than `relation.find`). --- activerecord/lib/active_record/persistence.rb | 92 ++++++++++++++++++++++++++ activerecord/lib/active_record/querying.rb | 2 +- activerecord/lib/active_record/relation.rb | 94 --------------------------- 3 files changed, 93 insertions(+), 95 deletions(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index fbbf9082cc..4a5ccdc597 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -71,6 +71,98 @@ module ActiveRecord klass.allocate.init_with("attributes" => attributes, "new_record" => false, &block) end + # Updates an object (or multiple objects) and saves it to the database, if validations pass. + # The resulting object is returned whether the object was saved successfully to the database or not. + # + # ==== Parameters + # + # * +id+ - This should be the id or an array of ids to be updated. + # * +attributes+ - This should be a hash of attributes or an array of hashes. + # + # ==== Examples + # + # # Updates one record + # Person.update(15, user_name: "Samuel", group: "expert") + # + # # Updates multiple records + # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } } + # Person.update(people.keys, people.values) + # + # # Updates multiple records from the result of a relation + # people = Person.where(group: "expert") + # people.update(group: "masters") + # + # Note: Updating a large number of records will run an UPDATE + # query for each record, which may cause a performance issue. + # When running callbacks is not needed for each record update, + # it is preferred to use {update_all}[rdoc-ref:Relation#update_all] + # for updating all records in a single query. + def update(id = :all, attributes) + if id.is_a?(Array) + id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) } + elsif id == :all + all.each { |record| record.update(attributes) } + else + if ActiveRecord::Base === id + raise ArgumentError, + "You are passing an instance of ActiveRecord::Base to `update`. " \ + "Please pass the id of the object by calling `.id`." + end + object = find(id) + object.update(attributes) + object + end + end + + # Destroy an object (or multiple objects) that has the given id. The object is instantiated first, + # therefore all callbacks and filters are fired off before the object is deleted. This method is + # less efficient than #delete but allows cleanup methods and other actions to be run. + # + # This essentially finds the object (or multiple objects) with the given id, creates a new object + # from the attributes, and then calls destroy on it. + # + # ==== Parameters + # + # * +id+ - Can be either an Integer or an Array of Integers. + # + # ==== Examples + # + # # Destroy a single object + # Todo.destroy(1) + # + # # Destroy multiple objects + # todos = [1,2,3] + # Todo.destroy(todos) + def destroy(id) + if id.is_a?(Array) + id.map { |one_id| destroy(one_id) } + else + find(id).destroy + end + end + + # Deletes the row with a primary key matching the +id+ argument, using a + # SQL +DELETE+ statement, and returns the number of rows deleted. Active + # Record objects are not instantiated, so the object's callbacks are not + # executed, including any :dependent association options. + # + # You can delete multiple rows at once by passing an Array of ids. + # + # Note: Although it is often much faster than the alternative, #destroy, + # skipping callbacks might bypass business logic in your application + # that ensures referential integrity or performs other essential jobs. + # + # ==== Examples + # + # # Delete a single row + # Todo.delete(1) + # + # # Delete multiple rows + # Todo.delete([2,3,4]) + def delete(id_or_array) + where(primary_key => id_or_array).delete_all + end + private # Called by +instantiate+ to decide which class to use for a new # record instance. diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index f780538319..3996d5661f 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -7,7 +7,7 @@ module ActiveRecord delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all delegate :find_by, :find_by!, to: :all - delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, to: :all + delegate :destroy_all, :delete_all, :update_all, to: :all delegate :find_each, :find_in_batches, :in_batches, to: :all delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or, :where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly, :extending, diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 42a9d8492b..f6825e67d7 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -376,50 +376,6 @@ module ActiveRecord @klass.connection.update stmt, "SQL" end - # Updates an object (or multiple objects) and saves it to the database, if validations pass. - # The resulting object is returned whether the object was saved successfully to the database or not. - # - # ==== Parameters - # - # * +id+ - This should be the id or an array of ids to be updated. - # * +attributes+ - This should be a hash of attributes or an array of hashes. - # - # ==== Examples - # - # # Updates one record - # Person.update(15, user_name: 'Samuel', group: 'expert') - # - # # Updates multiple records - # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } } - # Person.update(people.keys, people.values) - # - # # Updates multiple records from the result of a relation - # people = Person.where(group: 'expert') - # people.update(group: 'masters') - # - # Note: Updating a large number of records will run an - # UPDATE query for each record, which may cause a performance - # issue. When running callbacks is not needed for each record update, - # it is preferred to use #update_all for updating all records - # in a single query. - def update(id = :all, attributes) - if id.is_a?(Array) - id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) } - elsif id == :all - records.each { |record| record.update(attributes) } - else - if ActiveRecord::Base === id - 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 - end - object = find(id) - object.update(attributes) - object - end - end - # Destroys the records by instantiating each # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method. # Each object's callbacks are executed (including :dependent association options). @@ -440,33 +396,6 @@ module ActiveRecord records.each(&:destroy).tap { reset } end - # Destroy an object (or multiple objects) that has the given id. The object is instantiated first, - # therefore all callbacks and filters are fired off before the object is deleted. This method is - # less efficient than #delete but allows cleanup methods and other actions to be run. - # - # This essentially finds the object (or multiple objects) with the given id, creates a new object - # from the attributes, and then calls destroy on it. - # - # ==== Parameters - # - # * +id+ - Can be either an Integer or an Array of Integers. - # - # ==== Examples - # - # # Destroy a single object - # Todo.destroy(1) - # - # # Destroy multiple objects - # todos = [1,2,3] - # Todo.destroy(todos) - def destroy(id) - if id.is_a?(Array) - id.map { |one_id| destroy(one_id) } - else - find(id).destroy - end - end - # Deletes the records without instantiating the records # first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy] # method nor invoking callbacks. @@ -509,29 +438,6 @@ module ActiveRecord affected end - # Deletes the row with a primary key matching the +id+ argument, using a - # SQL +DELETE+ statement, and returns the number of rows deleted. Active - # Record objects are not instantiated, so the object's callbacks are not - # executed, including any :dependent association options. - # - # You can delete multiple rows at once by passing an Array of ids. - # - # Note: Although it is often much faster than the alternative, - # #destroy, skipping callbacks might bypass business logic in - # your application that ensures referential integrity or performs other - # essential jobs. - # - # ==== Examples - # - # # Delete a single row - # Todo.delete(1) - # - # # Delete multiple rows - # Todo.delete([2,3,4]) - def delete(id_or_array) - where(primary_key => id_or_array).delete_all - end - # Causes the records to be loaded from the database if they have not # been loaded already. You can use this if for some reason you need # to explicitly load some records before actually using them. The -- cgit v1.2.3 From 358360198e623229495b7175a2b3a0fe96a543ac Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 04:29:49 +0900 Subject: Ensure returning affected objects for class level `update` and `destroy` Class level `update` and `destroy` are using `find` in the internal, so it will raise `RecordNotFound` if given ids cannot find an object even though the method already affect (update or destroy) to any objects. These methods should return affected objects even in that case. --- activerecord/lib/active_record/persistence.rb | 6 ++++-- activerecord/test/cases/persistence_test.rb | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 4a5ccdc597..d1cb3783e7 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -99,7 +99,7 @@ module ActiveRecord # for updating all records in a single query. def update(id = :all, attributes) if id.is_a?(Array) - id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) } + id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }.compact elsif id == :all all.each { |record| record.update(attributes) } else @@ -112,6 +112,7 @@ module ActiveRecord object.update(attributes) object end + rescue RecordNotFound end # Destroy an object (or multiple objects) that has the given id. The object is instantiated first, @@ -135,10 +136,11 @@ module ActiveRecord # Todo.destroy(todos) def destroy(id) if id.is_a?(Array) - id.map { |one_id| destroy(one_id) } + id.map { |one_id| destroy(one_id) }.compact else find(id).destroy end + rescue RecordNotFound end # Deletes the row with a primary key matching the +id+ argument, using a diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 170fd02b6f..6cbe18cc8c 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -70,10 +70,10 @@ class PersistenceTest < ActiveRecord::TestCase end def test_update_many - topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } + topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, nil => {} } updated = Topic.update(topic_data.keys, topic_data.values) - assert_equal 2, updated.size + assert_equal [1, 2], updated.map(&:id) assert_equal "1 updated", Topic.find(1).content assert_equal "2 updated", Topic.find(2).content end @@ -81,9 +81,8 @@ class PersistenceTest < ActiveRecord::TestCase def test_class_level_update_is_affected_by_scoping topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } - assert_raise(ActiveRecord::RecordNotFound) do - Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } - end + assert_equal [], Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + assert_not_equal "1 updated", Topic.find(1).content assert_not_equal "2 updated", Topic.find(2).content end @@ -175,7 +174,7 @@ class PersistenceTest < ActiveRecord::TestCase clients = Client.all.merge!(order: "id").find([2, 3]) assert_difference("Client.count", -2) do - destroyed = Client.destroy([2, 3]).sort_by(&:id) + destroyed = Client.destroy([2, 3, nil]).sort_by(&:id) assert_equal clients, destroyed assert destroyed.all?(&:frozen?), "destroyed clients should be frozen" end @@ -917,7 +916,9 @@ class PersistenceTest < ActiveRecord::TestCase should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") Topic.find(1).replies << should_be_destroyed_reply - Topic.destroy(1) + topic = Topic.destroy(1) + assert topic.destroyed? + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) } end @@ -926,9 +927,8 @@ class PersistenceTest < ActiveRecord::TestCase should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") Topic.find(1).replies << should_not_be_destroyed_reply - assert_raise(ActiveRecord::RecordNotFound) do - Topic.where("1=0").scoping { Topic.destroy(1) } - end + assert_nil Topic.where("1=0").scoping { Topic.destroy(1) } + assert_nothing_raised { Topic.find(1) } assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) } end -- cgit v1.2.3 From 37be1cb47755a3d0a02eed12ac8cfb377b172f2d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 08:22:04 +0900 Subject: `id` (primary key) is not only an integer [ci skip] --- activerecord/lib/active_record/persistence.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index d1cb3783e7..b48a137a73 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -124,7 +124,7 @@ module ActiveRecord # # ==== Parameters # - # * +id+ - Can be either an Integer or an Array of Integers. + # * +id+ - This should be the id or an array of ids to be destroyed. # # ==== Examples # -- cgit v1.2.3 From 9b53f74d897d346f44caae7b0cffcd900f986259 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 04:55:51 +0900 Subject: Remove the code that swapping `scope` and `options` `options` is never assigned to `scope` as long as using splat hash. --- activerecord/lib/active_record/associations.rb | 6 +++--- activerecord/lib/active_record/associations/builder/association.rb | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a61c0336db..ef26f4a20c 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1400,7 +1400,7 @@ module ActiveRecord # has_many :tags, as: :taggable # has_many :reports, -> { readonly } # has_many :subscribers, through: :subscriptions, source: :user - def has_many(name, scope = nil, options = {}, &extension) + def has_many(name, scope = nil, **options, &extension) reflection = Builder::HasMany.build(self, name, scope, options, &extension) Reflection.add_reflection self, name, reflection end @@ -1534,7 +1534,7 @@ module ActiveRecord # has_one :club, through: :membership # has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable # has_one :credit_card, required: true - def has_one(name, scope = nil, options = {}) + def has_one(name, scope = nil, **options) reflection = Builder::HasOne.build(self, name, scope, options) Reflection.add_reflection self, name, reflection end @@ -1678,7 +1678,7 @@ module ActiveRecord # belongs_to :company, touch: :employees_last_updated_at # belongs_to :user, optional: true # belongs_to :account, default: -> { company.account } - def belongs_to(name, scope = nil, options = {}) + def belongs_to(name, scope = nil, **options) reflection = Builder::BelongsTo.build(self, name, scope, options) Reflection.add_reflection self, name, reflection end diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb index 496b16b58f..ca3032d967 100644 --- a/activerecord/lib/active_record/associations/builder/association.rb +++ b/activerecord/lib/active_record/associations/builder/association.rb @@ -38,11 +38,6 @@ module ActiveRecord::Associations::Builder # :nodoc: def self.create_reflection(model, name, scope, options, extension = nil) raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol) - if scope.is_a?(Hash) - options = scope - scope = nil - end - validate_options(options) scope = build_scope(scope, extension) -- cgit v1.2.3 From a714117736530871528857c5adfb5520e387aca5 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 11 Sep 2017 05:24:37 +0900 Subject: Fix collided sequence name detection If collided named sequence already exists, newly created serial column will generate alternative sequence name. Fix sequence name detection to allow the alternative names. --- .../connection_adapters/postgresql/column.rb | 9 +++++- .../test/cases/adapters/postgresql/serial_test.rb | 36 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb index 1b67cee24b..ff95fa4a0e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb @@ -10,8 +10,15 @@ module ActiveRecord def serial? return unless default_function - %r{\Anextval\('"?#{table_name}_#{name}_seq"?'::regclass\)\z} === default_function + if %r{\Anextval\('"?(?.+_(?seq\d*))"?'::regclass\)\z} =~ default_function + sequence_name_from_parts(table_name, name, suffix) == sequence_name + end end + + private + def sequence_name_from_parts(table_name, column_name, suffix) + "#{table_name}_#{column_name}_#{suffix}" + end end end end diff --git a/activerecord/test/cases/adapters/postgresql/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb index 3c020a88d0..df7875dbf2 100644 --- a/activerecord/test/cases/adapters/postgresql/serial_test.rb +++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb @@ -86,3 +86,39 @@ class PostgresqlBigSerialTest < ActiveRecord::PostgreSQLTestCase assert_match %r{t\.bigint\s+"serials_id",\s+default: -> \{ "nextval\('postgresql_big_serials_id_seq'::regclass\)" \}$}, output end end + +module SequenceNameDetectionTestCases + class CollidedSequenceNameTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + + def setup + @connection = ActiveRecord::Base.connection + @connection.create_table :foo_bar, force: true do |t| + t.serial :baz_id + end + @connection.create_table :foo, force: true do |t| + t.serial :bar_id + t.bigserial :bar_baz_id + end + end + + def teardown + @connection.drop_table :foo_bar, if_exists: true + @connection.drop_table :foo, if_exists: true + end + + def test_serial_columns + columns = @connection.columns(:foo) + columns.each do |column| + assert_equal :integer, column.type + assert column.serial? + end + end + + def test_schema_dump_with_collided_sequence_name + output = dump_table_schema "foo" + assert_match %r{t\.serial\s+"bar_id",\s+null: false$}, output + assert_match %r{t\.bigserial\s+"bar_baz_id",\s+null: false$}, output + end + end +end -- cgit v1.2.3 From 0993cbe3e0564c8dfa8b258e3a88059d311a352b Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Mon, 18 Sep 2017 09:13:00 +0900 Subject: Remove unused require in ActiveStorage::Variation --- activestorage/app/models/active_storage/variation.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb index bf269e2a8f..cf04a879eb 100644 --- a/activestorage/app/models/active_storage/variation.rb +++ b/activestorage/app/models/active_storage/variation.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "active_support/core_ext/object/inclusion" - # A set of transformations that can be applied to a blob to create a variant. This class is exposed via # the ActiveStorage::Blob#variant method and should rarely be used directly. # -- cgit v1.2.3 From 9b1115ea34e44fe772272ec58ccba7a9cbc0955a Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 11:03:59 +0900 Subject: Extract `associate_records_to_owner` to refactor `Preloader::Association` Since we have `Preloader#preload`, `Preloader::Association#preload` is a little confusing. And also, since the `preload` method is an abstract method, it is hard to read where `associated_records_by_owner` is called. This refactors `Preloader::Association` to ease to read where `associated_records_by_owner` is called. --- .../lib/active_record/associations/preloader/association.rb | 12 +++++++----- .../associations/preloader/collection_association.rb | 11 ++++------- .../associations/preloader/singular_association.rb | 11 +++-------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 9bb6a613e1..323f25e37e 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -17,11 +17,9 @@ module ActiveRecord end def run(preloader) - preload(preloader) - end - - def preload(preloader) - raise NotImplementedError + associated_records_by_owner(preloader).each do |owner, records| + associate_records_to_owner(owner, records) + end end # The name of the key on the associated records @@ -51,6 +49,10 @@ module ActiveRecord end end + def associate_records_to_owner(owner, records) + raise NotImplementedError + end + def owner_keys unless defined?(@owner_keys) @owner_keys = owners.map do |owner| diff --git a/activerecord/lib/active_record/associations/preloader/collection_association.rb b/activerecord/lib/active_record/associations/preloader/collection_association.rb index fb920a642c..fc2029f54a 100644 --- a/activerecord/lib/active_record/associations/preloader/collection_association.rb +++ b/activerecord/lib/active_record/associations/preloader/collection_association.rb @@ -5,13 +5,10 @@ module ActiveRecord class Preloader class CollectionAssociation < Association #:nodoc: private - - def preload(preloader) - associated_records_by_owner(preloader).each do |owner, records| - association = owner.association(reflection.name) - association.loaded! - association.target.concat(records) - end + def associate_records_to_owner(owner, records) + association = owner.association(reflection.name) + association.loaded! + association.target.concat(records) end end end diff --git a/activerecord/lib/active_record/associations/preloader/singular_association.rb b/activerecord/lib/active_record/associations/preloader/singular_association.rb index 266b5f6b1c..30a92411e3 100644 --- a/activerecord/lib/active_record/associations/preloader/singular_association.rb +++ b/activerecord/lib/active_record/associations/preloader/singular_association.rb @@ -5,14 +5,9 @@ module ActiveRecord class Preloader class SingularAssociation < Association #:nodoc: private - - def preload(preloader) - associated_records_by_owner(preloader).each do |owner, associated_records| - record = associated_records.first - - association = owner.association(reflection.name) - association.target = record - end + def associate_records_to_owner(owner, records) + association = owner.association(reflection.name) + association.target = records.first end end end -- cgit v1.2.3 From a1aa86b99e298c4a2b7a2e5da7a1bb9d8ba65a3b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 11:42:29 +0900 Subject: The name of the key on the owner is abstracted as `reflection.join_foreign_key` --- .../lib/active_record/associations/preloader/association.rb | 10 +++++----- .../lib/active_record/associations/preloader/belongs_to.rb | 4 ---- .../lib/active_record/associations/preloader/has_many.rb | 4 ---- .../lib/active_record/associations/preloader/has_one.rb | 4 ---- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 323f25e37e..1e2d6c4f1e 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -27,16 +27,16 @@ module ActiveRecord raise NotImplementedError end - # The name of the key on the model which declares the association - def owner_key_name - raise NotImplementedError - end - private def options reflection.options end + # The name of the key on the model which declares the association + def owner_key_name + reflection.join_foreign_key + end + def associated_records_by_owner(preloader) records = load_records do |record| owner = owners_by_key[convert_key(record[association_key_name])] diff --git a/activerecord/lib/active_record/associations/preloader/belongs_to.rb b/activerecord/lib/active_record/associations/preloader/belongs_to.rb index ae9695f26a..62df4f5530 100644 --- a/activerecord/lib/active_record/associations/preloader/belongs_to.rb +++ b/activerecord/lib/active_record/associations/preloader/belongs_to.rb @@ -7,10 +7,6 @@ module ActiveRecord def association_key_name options[:primary_key] || klass && klass.primary_key end - - def owner_key_name - reflection.foreign_key - end end end end diff --git a/activerecord/lib/active_record/associations/preloader/has_many.rb b/activerecord/lib/active_record/associations/preloader/has_many.rb index 29a1ce099d..9ff0aa24ab 100644 --- a/activerecord/lib/active_record/associations/preloader/has_many.rb +++ b/activerecord/lib/active_record/associations/preloader/has_many.rb @@ -7,10 +7,6 @@ module ActiveRecord def association_key_name reflection.foreign_key end - - def owner_key_name - reflection.active_record_primary_key - end end end end diff --git a/activerecord/lib/active_record/associations/preloader/has_one.rb b/activerecord/lib/active_record/associations/preloader/has_one.rb index d87abf630f..e58fa351b0 100644 --- a/activerecord/lib/active_record/associations/preloader/has_one.rb +++ b/activerecord/lib/active_record/associations/preloader/has_one.rb @@ -7,10 +7,6 @@ module ActiveRecord def association_key_name reflection.foreign_key end - - def owner_key_name - reflection.active_record_primary_key - end end end end -- cgit v1.2.3 From dae16f8cd5908a0accfaf280d89b47b757c4bf2d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 12:14:20 +0900 Subject: The name of the key on the associated record is abstracted as `reflection.join_primary_key` --- .../associations/preloader/association.rb | 10 +++++----- .../active_record/associations/preloader/belongs_to.rb | 3 --- .../active_record/associations/preloader/has_many.rb | 3 --- .../active_record/associations/preloader/has_one.rb | 3 --- activerecord/lib/active_record/reflection.rb | 18 +++++++++--------- 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 1e2d6c4f1e..8101c5604b 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -22,16 +22,16 @@ module ActiveRecord end end - # The name of the key on the associated records - def association_key_name - raise NotImplementedError - end - private def options reflection.options end + # The name of the key on the associated records + def association_key_name + reflection.join_primary_key(klass) + end + # The name of the key on the model which declares the association def owner_key_name reflection.join_foreign_key diff --git a/activerecord/lib/active_record/associations/preloader/belongs_to.rb b/activerecord/lib/active_record/associations/preloader/belongs_to.rb index 62df4f5530..a8e3340b23 100644 --- a/activerecord/lib/active_record/associations/preloader/belongs_to.rb +++ b/activerecord/lib/active_record/associations/preloader/belongs_to.rb @@ -4,9 +4,6 @@ module ActiveRecord module Associations class Preloader class BelongsTo < SingularAssociation #:nodoc: - def association_key_name - options[:primary_key] || klass && klass.primary_key - end end end end diff --git a/activerecord/lib/active_record/associations/preloader/has_many.rb b/activerecord/lib/active_record/associations/preloader/has_many.rb index 9ff0aa24ab..72f55bc43f 100644 --- a/activerecord/lib/active_record/associations/preloader/has_many.rb +++ b/activerecord/lib/active_record/associations/preloader/has_many.rb @@ -4,9 +4,6 @@ module ActiveRecord module Associations class Preloader class HasMany < CollectionAssociation #:nodoc: - def association_key_name - reflection.foreign_key - end end end end diff --git a/activerecord/lib/active_record/associations/preloader/has_one.rb b/activerecord/lib/active_record/associations/preloader/has_one.rb index e58fa351b0..e339b65fb5 100644 --- a/activerecord/lib/active_record/associations/preloader/has_one.rb +++ b/activerecord/lib/active_record/associations/preloader/has_one.rb @@ -4,9 +4,6 @@ module ActiveRecord module Associations class Preloader class HasOne < SingularAssociation #:nodoc: - def association_key_name - reflection.foreign_key - end end end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 889e24dd1a..82ab2415e1 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -292,13 +292,17 @@ module ActiveRecord end def get_join_keys(association_klass) - JoinKeys.new(join_pk(association_klass), join_foreign_key) + JoinKeys.new(join_primary_key(association_klass), join_foreign_key) end def build_scope(table, predicate_builder = predicate_builder(table)) Relation.create(klass, table, predicate_builder) end + def join_primary_key(_) + foreign_key + end + def join_foreign_key active_record_primary_key end @@ -313,10 +317,6 @@ module ActiveRecord PredicateBuilder.new(TableMetadata.new(klass, table)) end - def join_pk(_) - foreign_key - end - def primary_key(klass) klass.primary_key || raise(UnknownPrimaryKey.new(klass)) end @@ -736,6 +736,10 @@ module ActiveRecord end end + def join_primary_key(klass) + polymorphic? ? association_primary_key(klass) : association_primary_key + end + def join_foreign_key foreign_key end @@ -745,10 +749,6 @@ module ActiveRecord def calculate_constructable(macro, options) !polymorphic? end - - def join_pk(klass) - polymorphic? ? association_primary_key(klass) : association_primary_key - end end class HasAndBelongsToManyReflection < AssociationReflection # :nodoc: -- cgit v1.2.3 From 8cde75813d54b92a894fb47d296929e8a6d26ba1 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 18 Sep 2017 12:29:44 +0900 Subject: Remove unused delegation to `reflection.options` in `Preloader::Association` --- activerecord/lib/active_record/associations/preloader/association.rb | 4 ---- .../lib/active_record/associations/preloader/through_association.rb | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 8101c5604b..fe696e0d6e 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -23,10 +23,6 @@ module ActiveRecord end private - def options - reflection.options - end - # The name of the key on the associated records def association_key_name reflection.join_primary_key(klass) diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index 53e9c14823..fa32cc5553 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -80,6 +80,7 @@ module ActiveRecord def through_scope scope = through_reflection.klass.unscoped + options = reflection.options if options[:source_type] scope.where! reflection.foreign_type => options[:source_type] -- cgit v1.2.3 From 76d373bc4196446a59c60e6c500def2c27e5b471 Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Mon, 18 Sep 2017 08:15:02 +0300 Subject: Remove "the" [ci skip] --- guides/source/security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/security.md b/guides/source/security.md index 2ac52155f8..746da5e634 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -1029,7 +1029,7 @@ Rails generates a `config/credentials.yml.enc` to store third-party credentials within the repo. This is only viable because Rails encrypts the file with a master key that's generated into a version control ignored `config/master.key` — Rails will also look for that key in `ENV["RAILS_MASTER_KEY"]`. Rails also requires the -the key to boot in production, so the credentials can be read. +key to boot in production, so the credentials can be read. To edit stored credentials use `bin/rails credentials:edit`. -- cgit v1.2.3 From 62d547c6a47c45c033597bb744854e5b2f477c7e Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Mon, 18 Sep 2017 09:15:33 +0300 Subject: Upcase js [ci skip] --- guides/source/caching_with_rails.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 910a531068..96650b5be9 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -181,7 +181,7 @@ cache. ### Shared Partial Caching -It is possible to share partials and associated caching between files with different mime types. For example shared partial caching allows template writers to share a partial between HTML and Javascript files. When templates are collected in the template resolver file paths they only include the template language extension and not the mime type. Because of this templates can be used for multiple mime types. Both HTML and JavaScript requests will respond to the following code: +It is possible to share partials and associated caching between files with different mime types. For example shared partial caching allows template writers to share a partial between HTML and JavaScript files. When templates are collected in the template resolver file paths they only include the template language extension and not the mime type. Because of this templates can be used for multiple mime types. Both HTML and JavaScript requests will respond to the following code: ```ruby render(partial: 'hotels/hotel', collection: @hotels, cached: true) @@ -195,7 +195,7 @@ Another option is to include the full filename of the partial to render. render(partial: 'hotels/hotel.html.erb', collection: @hotels, cached: true) ``` -Will load a file named `hotels/hotel.html.erb` in any file mime type, for example you could include this partial in a Javascript file. +Will load a file named `hotels/hotel.html.erb` in any file mime type, for example you could include this partial in a JavaScript file. ### Managing dependencies -- cgit v1.2.3 From c9527652c753dd8fe3ac120a05f5e80d6d691346 Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Mon, 18 Sep 2017 09:38:47 +0300 Subject: Fix quotes [ci skip] --- actioncable/README.md | 4 ++-- guides/source/autoloading_and_reloading_constants.md | 8 ++++---- guides/source/working_with_javascript_in_rails.md | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/actioncable/README.md b/actioncable/README.md index a060e8938e..70b39ead57 100644 --- a/actioncable/README.md +++ b/actioncable/README.md @@ -454,9 +454,9 @@ The Ruby side of things is built on top of [websocket-driver](https://github.com ## Deployment Action Cable is powered by a combination of WebSockets and threads. All of the -connection management is handled internally by utilizing Ruby’s native thread +connection management is handled internally by utilizing Ruby's native thread support, which means you can use all your regular Rails models with no problems -as long as you haven’t committed any thread-safety sins. +as long as you haven't committed any thread-safety sins. The Action Cable server does _not_ need to be a multi-threaded application server. This is because Action Cable uses the [Rack socket hijacking API](http://www.rubydoc.info/github/rack/rack/file/SPEC#Hijacking) diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md index c62194faf4..ede0324a51 100644 --- a/guides/source/autoloading_and_reloading_constants.md +++ b/guides/source/autoloading_and_reloading_constants.md @@ -954,7 +954,7 @@ to work on some subclass, things get interesting. While working with `Polygon` you do not need to be aware of all its descendants, because anything in the table is by definition a polygon, but when working with subclasses Active Record needs to be able to enumerate the types it is looking -for. Let’s see an example. +for. Let's see an example. `Rectangle.all` only loads rectangles by adding a type constraint to the query: @@ -963,7 +963,7 @@ SELECT "polygons".* FROM "polygons" WHERE "polygons"."type" IN ("Rectangle") ``` -Let’s introduce now a subclass of `Rectangle`: +Let's introduce now a subclass of `Rectangle`: ```ruby # app/models/square.rb @@ -978,7 +978,7 @@ SELECT "polygons".* FROM "polygons" WHERE "polygons"."type" IN ("Rectangle", "Square") ``` -But there’s a caveat here: How does Active Record know that the class `Square` +But there's a caveat here: How does Active Record know that the class `Square` exists at all? Even if the file `app/models/square.rb` exists and defines the `Square` class, @@ -1049,7 +1049,7 @@ end The purpose of this setup would be that the application uses the class that corresponds to the environment via `AUTH_SERVICE`. In development mode -`MockedAuthService` gets autoloaded when the initializer runs. Let’s suppose +`MockedAuthService` gets autoloaded when the initializer runs. Let's suppose we do some requests, change its implementation, and hit the application again. To our surprise the changes are not reflected. Why? diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 27cef2bd27..098366ec1b 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -382,7 +382,7 @@ Rails 5.1 introduced rails-ujs and dropped jQuery as a dependency. As a result the Unobtrusive JavaScript (UJS) driver has been rewritten to operate without jQuery. These introductions cause small changes to `custom events` fired during the request: -NOTE: Signature of calls to UJS’s event handlers has changed. +NOTE: Signature of calls to UJS's event handlers has changed. Unlike the version with jQuery, all custom events return only one parameter: `event`. In this parameter, there is an additional attribute `detail` which contains an array of extra parameters. -- cgit v1.2.3 From 5c13e8c98e6e284afa4f98e198c101dae5d78230 Mon Sep 17 00:00:00 2001 From: Gerard Cahill Date: Mon, 18 Sep 2017 10:47:48 +0100 Subject: add section to guides for discarding and retrying active jobs [ci skip] --- guides/source/active_job_basics.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index 7a3ff12b63..f94e9fe197 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -389,6 +389,25 @@ class GuestsCleanupJob < ApplicationJob end ``` +### Retrying or Discarding failed jobs + +It's also possible to retry or discard a job if an exception is raised during execution. +For example: + +```ruby +class RemoteServiceJob < ActiveJob::Base + retry_on CustomAppException # defaults to 3s wait, 5 attempts + + discard_on ActiveJob::DeserializationError + + def perform(*args) + # Might raise CustomAppException or ActiveJob::DeserializationError + end +end +``` + +To get more details see the API Documentation for [ActiveJob::Exceptions](http://api.rubyonrails.org/classes/ActiveJob/Exceptions/ClassMethods.html). + ### Deserialization GlobalID allows serializing full Active Record objects passed to `#perform`. -- cgit v1.2.3 From 9291a813694b4b443557c282ba1fbb0c8b909d27 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Mon, 18 Sep 2017 22:15:46 +0900 Subject: Use ApplicationJob instead of ActiveJob::Base in guide [ci skip] --- guides/source/active_job_basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index f94e9fe197..914ef2c327 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -395,7 +395,7 @@ It's also possible to retry or discard a job if an exception is raised during ex For example: ```ruby -class RemoteServiceJob < ActiveJob::Base +class RemoteServiceJob < ApplicationJob retry_on CustomAppException # defaults to 3s wait, 5 attempts discard_on ActiveJob::DeserializationError -- cgit v1.2.3 From ba04b580c6e56e366b1b8e4ef003e9a0c1c419bb Mon Sep 17 00:00:00 2001 From: Thomas Walpole Date: Sun, 17 Sep 2017 15:04:20 -0700 Subject: Use the default Capybara registered puma server configuration --- Gemfile | 2 +- Gemfile.lock | 2 +- actionpack/CHANGELOG.md | 7 +++++++ actionpack/lib/action_dispatch/system_testing/server.rb | 14 +------------- actionpack/test/dispatch/system_testing/server_test.rb | 4 ---- railties/lib/rails/generators/rails/app/templates/Gemfile | 2 +- 6 files changed, 11 insertions(+), 20 deletions(-) diff --git a/Gemfile b/Gemfile index fda547fab2..c60241d447 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,7 @@ gem "rake", ">= 11.1" # be loaded after loading the test library. gem "mocha", "~> 0.14", require: false -gem "capybara", "~> 2.13" +gem "capybara", "~> 2.15" gem "rack-cache", "~> 1.2" gem "jquery-rails" diff --git a/Gemfile.lock b/Gemfile.lock index 787631ada5..fb18fdc14e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -490,7 +490,7 @@ DEPENDENCIES blade-sauce_labs_plugin bootsnap (>= 1.1.0) byebug - capybara (~> 2.13) + capybara (~> 2.15) coffee-rails dalli (>= 2.2.1) delayed_job diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 932968fa35..a53d8efee1 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,10 @@ +* Use Capybara registered `:puma` server config. + + The Capybara registered `:puma` server ensures the puma server is run in process so + connection sharing and open request detection work correctly by default. + + *Thomas Walpole* + * Cookies `:expires` option supports `ActiveSupport::Duration` object. cookies[:user_name] = { value: "assain", expires: 1.hour } diff --git a/actionpack/lib/action_dispatch/system_testing/server.rb b/actionpack/lib/action_dispatch/system_testing/server.rb index 76bada8df1..32aa6a4dc4 100644 --- a/actionpack/lib/action_dispatch/system_testing/server.rb +++ b/actionpack/lib/action_dispatch/system_testing/server.rb @@ -12,29 +12,17 @@ module ActionDispatch self.silence_puma = false def run - register setup end private - def register - Capybara.register_server :rails_puma do |app, port, host| - Rack::Handler::Puma.run( - app, - Port: port, - Threads: "0:1", - Silent: self.class.silence_puma - ) - end - end - def setup set_server set_port end def set_server - Capybara.server = :rails_puma + Capybara.server = :puma, { Silent: self.class.silence_puma } end def set_port diff --git a/actionpack/test/dispatch/system_testing/server_test.rb b/actionpack/test/dispatch/system_testing/server_test.rb index ed65d93e49..1866225fc1 100644 --- a/actionpack/test/dispatch/system_testing/server_test.rb +++ b/actionpack/test/dispatch/system_testing/server_test.rb @@ -9,10 +9,6 @@ class ServerTest < ActiveSupport::TestCase ActionDispatch::SystemTesting::Server.new.run end - test "initializing the server port" do - assert_includes Capybara.servers, :rails_puma - end - test "port is always included" do assert Capybara.always_include_port, "expected Capybara.always_include_port to be true" end diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index 770247d8ca..bfbba789b0 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -41,7 +41,7 @@ group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] <%- if depends_on_system_test? -%> # Adds support for Capybara system testing and selenium driver - gem 'capybara', '~> 2.13' + gem 'capybara', '~> 2.15' gem 'selenium-webdriver' <%- end -%> end -- cgit v1.2.3 From 704bf9b9c64a2f92c6e72d74350bd8f00aaca5c5 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sun, 17 Sep 2017 18:24:16 +0900 Subject: Add `with_attached_*` scope to `has_one_attached` macro * For avoiding N+1 problem, added `with_attached_*` scope to `has_one_attached` macro. --- activestorage/lib/active_storage/attached/macros.rb | 6 ++++++ activestorage/test/models/attachments_test.rb | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb index 35a081adc4..f0256718ac 100644 --- a/activestorage/lib/active_storage/attached/macros.rb +++ b/activestorage/lib/active_storage/attached/macros.rb @@ -12,6 +12,10 @@ module ActiveStorage # There is no column defined on the model side, Active Storage takes # care of the mapping between your records and the attachment. # + # To avoid N+1 queries, you can include the attached blobs in your query like so: + # + # User.with_attached_avatar + # # Under the covers, this relationship is implemented as a +has_one+ association to a # ActiveStorage::Attachment record and a +has_one-through+ association to a # ActiveStorage::Blob record. These associations are available as +avatar_attachment+ @@ -33,6 +37,8 @@ module ActiveStorage has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record has_one :"#{name}_blob", through: :"#{name}_attachment", class_name: "ActiveStorage::Blob", source: :blob + scope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) } + if dependent == :purge_later before_destroy { public_send(name).purge_later } end diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index ac346c0087..379ae0a416 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -84,6 +84,19 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase end end + test "find with attached blob" do + records = %w[alice bob].map do |name| + User.create!(name: name).tap do |user| + user.avatar.attach create_blob(filename: "#{name}.jpg") + end + end + + users = User.where(id: records.map(&:id)).with_attached_avatar.all + + assert_equal "alice.jpg", users.first.avatar.filename.to_s + assert_equal "bob.jpg", users.second.avatar.filename.to_s + end + test "attach existing blobs" do @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg") -- cgit v1.2.3 From fe15175039780e689b5ddc9fffeb0a559432cc3b Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Tue, 19 Sep 2017 00:02:47 +0300 Subject: Update action_controller_overview.md [ci skip] --- guides/source/action_controller_overview.md | 4 ++-- guides/source/getting_started.md | 2 +- guides/source/i18n.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 2c3f74c3e1..24366ae6e9 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -401,8 +401,8 @@ Rails sets up (for the CookieStore) a secret key used for signing the session da ```ruby # amazon: -# access_key_id: 123 -# secret_access_key: 345 +# access_key_id: 123 +# secret_access_key: 345 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: 492f... diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 7c7b3a4c01..70a945ad9e 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -594,7 +594,7 @@ familiar error: You now need to create the `create` action within the `ArticlesController` for this to work. -NOTE: by default `form_with` submits forms using Ajax thereby skipping full page +NOTE: By default `form_with` submits forms using Ajax thereby skipping full page redirects. To make this guide easier to get into we've disabled that with `local: true` for now. diff --git a/guides/source/i18n.md b/guides/source/i18n.md index cb24822f86..0153f52249 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -1187,7 +1187,7 @@ If you find your own locale (language) missing from our [example translations da Resources --------- -* [Google group: rails-i18n](https://groups.google.com/forum/#!forum/rails-i18n) - The project's mailing list. +* [Google group: rails-i18n](https://groups.google.com/group/rails-i18n) - The project's mailing list. * [GitHub: rails-i18n](https://github.com/svenfuchs/rails-i18n) - Code repository and issue tracker for the rails-i18n project. Most importantly you can find lots of [example translations](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) for Rails that should work for your application in most cases. * [GitHub: i18n](https://github.com/svenfuchs/i18n) - Code repository and issue tracker for the i18n gem. -- cgit v1.2.3 From 5533696015e256dfb61c62bc1d34eeb1b2b1cdf6 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Tue, 19 Sep 2017 18:27:33 +0900 Subject: Remove unused `NumberToRoundedConverter#digits_and_rounded_number` `digits_and_rounded_number` is unused since #26628 --- .../number_helper/number_to_rounded_converter.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb index 3b62fe6819..b7ad76bb62 100644 --- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb @@ -37,18 +37,6 @@ module ActiveSupport private - def digits_and_rounded_number(precision) - if zero? - [1, 0] - else - digits = digit_count(number) - multiplier = 10**(digits - precision) - rounded_number = calculate_rounded_number(multiplier) - digits = digit_count(rounded_number) # After rounding, the number of digits may have changed - [digits, rounded_number] - end - end - def calculate_rounded_number(multiplier) (number / BigDecimal.new(multiplier.to_f.to_s)).round * multiplier end -- cgit v1.2.3 From 7bab6b8cd950be79c040377b43b6ed6030fb98f4 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Kinjo Date: Wed, 20 Sep 2017 00:09:17 +0900 Subject: Add :comment option for add_column [ci skip] --- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 ++ 1 file changed, 2 insertions(+) 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 f57c7a5d4d..c9607df28c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -522,6 +522,8 @@ module ActiveRecord # Specifies the precision for the :decimal and :numeric columns. # * :scale - # Specifies the scale for the :decimal and :numeric columns. + # * :comment - + # Specifies the comment for the column. This option is ignored by some backends. # # Note: The precision is the total number of significant digits, # and the scale is the number of digits that can be stored following -- cgit v1.2.3 From 7ee6cd9dfce04599502c57bfd8b3d862df659938 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Wed, 20 Sep 2017 00:37:52 +0900 Subject: Use RESTful instead of restful --- railties/lib/rails/generators/rails/resource/USAGE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/resource/USAGE b/railties/lib/rails/generators/rails/resource/USAGE index e359cd574f..66d0ee546a 100644 --- a/railties/lib/rails/generators/rails/resource/USAGE +++ b/railties/lib/rails/generators/rails/resource/USAGE @@ -1,6 +1,6 @@ Description: Stubs out a new resource including an empty model and controller suitable - for a restful, resource-oriented application. Pass the singular model name, + for a RESTful, resource-oriented application. Pass the singular model name, either CamelCased or under_scored, as the first argument, and an optional list of attribute pairs. -- cgit v1.2.3 From 2f8ecdb21d110ae2f862582cc31e5a10f487a3b7 Mon Sep 17 00:00:00 2001 From: Claudio B Date: Tue, 19 Sep 2017 15:08:32 -0700 Subject: Use credentials, not secrets, for Active Storage (#30650) According to #30067: > This PR will deprecate secrets.yml* and instead adopt > config/credentials.yml.enc to signify what these secrets are specifically > for: Keeping API keys, database passwords, and any other integration > credentials in one place. [ci skip] since only comments are being edited. --- guides/source/action_controller_overview.md | 2 +- .../lib/rails/generators/rails/app/templates/config/storage.yml | 8 ++++---- .../rails/generators/rails/credentials/credentials_generator.rb | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 24366ae6e9..5fb8e300de 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -400,7 +400,7 @@ Rails.application.config.session_store :cookie_store, key: '_your_app_session', Rails sets up (for the CookieStore) a secret key used for signing the session data in `config/credentials.yml.enc`. This can be changed with `bin/rails credentials:edit`. ```ruby -# amazon: +# aws: # access_key_id: 123 # secret_access_key: 345 diff --git a/railties/lib/rails/generators/rails/app/templates/config/storage.yml b/railties/lib/rails/generators/rails/app/templates/config/storage.yml index 089ed4567a..9ad779d334 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/storage.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml @@ -6,10 +6,10 @@ local: service: Disk root: <%%= Rails.root.join("storage") %> -# Use rails secrets:edit to set the AWS secrets (as shared:aws:access_key_id|secret_access_key) +# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) # amazon: # service: S3 -# access_key_id: <%%= Rails.application.secrets.dig(:aws, :access_key_id) %> +# access_key_id: <%%= Rails.application.credentials.dig(:aws, :access_key_id) %> # secret_access_key: <%%= Rails.application.secrets.dig(:aws, :secret_access_key) %> # region: us-east-1 # bucket: your_own_bucket @@ -21,12 +21,12 @@ local: # keyfile: <%%= Rails.root.join("path/to/gcs.keyfile") %> # bucket: your_own_bucket -# Use rails secrets:edit to set the Azure Storage secret (as shared:azure_storage:storage_access_key) +# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) # microsoft: # service: AzureStorage # path: your_azure_storage_path # storage_account_name: your_account_name -# storage_access_key: <%%= Rails.application.secrets.dig(:azure_storage, :storage_access_key) %> +# storage_access_key: <%%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> # container: your_container_name # mirror: diff --git a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb index ddcccd5ce5..21ca566818 100644 --- a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb +++ b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb @@ -37,7 +37,7 @@ module Rails private def credentials_template - "# amazon:\n# access_key_id: 123\n# secret_access_key: 345\n\n" + + "# aws:\n# access_key_id: 123\n# secret_access_key: 345\n\n" + "# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.\n" + "secret_key_base: #{SecureRandom.hex(64)}" end -- cgit v1.2.3 From 96fe56357a4bb61e2c5aeaceb6021ad07cacad45 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 20 Sep 2017 08:10:03 +0900 Subject: Use credentials in `storage.yml` template Follow up of #30650 --- railties/lib/rails/generators/rails/app/templates/config/storage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/storage.yml b/railties/lib/rails/generators/rails/app/templates/config/storage.yml index 9ad779d334..9bada4b66d 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/storage.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml @@ -10,7 +10,7 @@ local: # amazon: # service: S3 # access_key_id: <%%= Rails.application.credentials.dig(:aws, :access_key_id) %> -# secret_access_key: <%%= Rails.application.secrets.dig(:aws, :secret_access_key) %> +# secret_access_key: <%%= Rails.application.credentials.dig(:aws, :secret_access_key) %> # region: us-east-1 # bucket: your_own_bucket -- cgit v1.2.3 From 0c8bed9f848446a6876c27781a38badb2c916be4 Mon Sep 17 00:00:00 2001 From: Yuki Masutomi Date: Tue, 19 Sep 2017 18:51:30 +0900 Subject: make create_join_table compatible. --- .../lib/active_record/migration/compatibility.rb | 15 +++++++++++ .../test/cases/migration/compatibility_test.rb | 30 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index 784292f3f9..87c1c58aff 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -71,6 +71,21 @@ module ActiveRecord end end + def create_join_table(table_1, table_2, column_options: {}, **options) + column_options.reverse_merge!(type: :integer) + + if block_given? + super(table_1, table_2, column_options: column_options, **options) do |t| + class << t + prepend TableDefinition + end + yield t + end + else + super + end + end + def add_reference(table_name, ref_name, **options) super(table_name, ref_name, type: :integer, **options) end diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index cb3b02c02a..e1311c0f21 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -199,6 +199,36 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase assert_match %r{create_table "legacy_primary_keys", id: :integer, default: nil}, schema end + def test_legacy_join_table_foreign_keys_should_be_integer + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_join_table :apples, :bananas do |t| + end + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "apples_bananas" + assert_match %r{integer "apple_id", null: false}, schema + assert_match %r{integer "banana_id", null: false}, schema + end + + def test_legacy_join_table_column_options_should_be_overwritten + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_join_table :apples, :bananas, column_options: { type: :bigint } do |t| + end + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "apples_bananas" + assert_match %r{bigint "apple_id", null: false}, schema + assert_match %r{bigint "banana_id", null: false}, schema + end + if current_adapter?(:Mysql2Adapter) def test_legacy_bigint_primary_key_should_be_auto_incremented @migration = Class.new(ActiveRecord::Migration[5.0]) { -- cgit v1.2.3 From 80f46653e2353a40a7175cbd6030dcf60916d6cd Mon Sep 17 00:00:00 2001 From: Manoj M J Date: Wed, 20 Sep 2017 13:08:00 +0530 Subject: Fix error message documentation --- activesupport/lib/active_support/ordered_options.rb | 4 ++-- guides/source/security.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index fa7825b3ba..c2a37fbdd7 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -24,7 +24,7 @@ module ActiveSupport # To raise an exception when the value is blank, append a # bang to the key name, like: # - # h.dog! # => raises KeyError: key not found: :dog + # h.dog! # => raises KeyError: :dog is blank # class OrderedOptions < Hash alias_method :_get, :[] # preserve the original #[] method @@ -46,7 +46,7 @@ module ActiveSupport bangs = name_string.chomp!("!") if bangs - fetch(name_string.to_sym).presence || raise(KeyError.new("#{name_string} is blank.")) + fetch(name_string.to_sym).presence || raise(KeyError.new(":#{name_string} is blank")) else self[name_string] end diff --git a/guides/source/security.md b/guides/source/security.md index 2ac52155f8..b19a04452a 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -1049,7 +1049,7 @@ If you want an exception to be raised when some key is blank, use the bang version: ```ruby -Rails.application.credentials.some_api_key! # => raises KeyError: key not found: :some_api_key +Rails.application.credentials.some_api_key! # => raises KeyError: :some_api_key is blank ``` Additional Resources -- cgit v1.2.3 From 91edf754c4ccdb55bfebb2fcb1458ca0a4d769d9 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Wed, 20 Sep 2017 15:34:04 -0400 Subject: Flesh out ActiveStorage::Filename docs --- activestorage/app/models/active_storage/filename.rb | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb index dead6b6d33..8e3cd488a4 100644 --- a/activestorage/app/models/active_storage/filename.rb +++ b/activestorage/app/models/active_storage/filename.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -# Encapsulates a string representing a filename to provide convenience access to parts of it and a sanitized version. -# This is what's returned by ActiveStorage::Blob#filename. A Filename instance is comparable so it can be used for sorting. +# Encapsulates a string representing a filename to provide convenient access to parts of it sanitization. +# A Filename instance is returned by ActiveStorage::Blob#filename, and is comparable so it can be used for sorting. class ActiveStorage::Filename include Comparable @@ -9,23 +9,31 @@ class ActiveStorage::Filename @filename = filename end - # Returns the basename of the filename. + # Returns the part of the filename preceding any extension. # # ActiveStorage::Filename.new("racecar.jpg").base # => "racecar" + # ActiveStorage::Filename.new("racecar").base # => "racecar" + # ActiveStorage::Filename.new(".gitignore").base # => ".gitignore" def base File.basename @filename, extension_with_delimiter end - # Returns the extension with delimiter of the filename. + # Returns the extension of the filename (i.e. the substring following the last dot, excluding a dot at the + # beginning) with the dot that precedes it. If the filename has no extension, an empty string is returned. # # ActiveStorage::Filename.new("racecar.jpg").extension_with_delimiter # => ".jpg" + # ActiveStorage::Filename.new("racecar").extension_with_delimiter # => "" + # ActiveStorage::Filename.new(".gitignore").extension_with_delimiter # => "" def extension_with_delimiter File.extname @filename end - # Returns the extension without delimiter of the filename. + # Returns the extension of the filename (i.e. the substring following the last dot, excluding a dot at + # the beginning). If the filename has no extension, an empty string is returned. # # ActiveStorage::Filename.new("racecar.jpg").extension_without_delimiter # => "jpg" + # ActiveStorage::Filename.new("racecar").extension_without_delimiter # => "" + # ActiveStorage::Filename.new(".gitignore").extension_without_delimiter # => "" def extension_without_delimiter extension_with_delimiter.from(1).to_s end @@ -37,7 +45,7 @@ class ActiveStorage::Filename # ActiveStorage::Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg" # ActiveStorage::Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg" # - # ...and any other character unsafe for URLs or storage is converted or stripped. + # Characters considered unsafe for storage (e.g. \, $, and the RTL override character) are replaced with a dash. def sanitized @filename.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-") end -- cgit v1.2.3 From 4e68525e338685c1c77b76a488eb06af021f0e05 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Wed, 20 Sep 2017 15:35:01 -0400 Subject: Add missing word [ci skip] --- activestorage/app/models/active_storage/filename.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb index 8e3cd488a4..79d55dc889 100644 --- a/activestorage/app/models/active_storage/filename.rb +++ b/activestorage/app/models/active_storage/filename.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Encapsulates a string representing a filename to provide convenient access to parts of it sanitization. +# Encapsulates a string representing a filename to provide convenient access to parts of it and sanitization. # A Filename instance is returned by ActiveStorage::Blob#filename, and is comparable so it can be used for sorting. class ActiveStorage::Filename include Comparable -- cgit v1.2.3 From 8c2ca4d4fc0084376a917429f9ad389f1e209909 Mon Sep 17 00:00:00 2001 From: Anton Chumakov Date: Wed, 20 Sep 2017 22:37:19 +0300 Subject: Update Rails Guides about :autosave option --- guides/source/association_basics.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index b5bd24d027..9616647f15 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -906,7 +906,7 @@ The `belongs_to` association supports these options: ##### `:autosave` -If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object. +If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved. ##### `:class_name` @@ -1257,7 +1257,7 @@ Setting the `:as` option indicates that this is a polymorphic association. Polym ##### `:autosave` -If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object. +If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved. ##### `:class_name` @@ -1653,7 +1653,7 @@ Setting the `:as` option indicates that this is a polymorphic association, as di ##### `:autosave` -If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object. +If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved. ##### `:class_name` @@ -2176,7 +2176,7 @@ end ##### `:autosave` -If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object. +If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved. ##### `:class_name` -- cgit v1.2.3 From fda9df7b4e328ed2bfc3dad05899ef6269495688 Mon Sep 17 00:00:00 2001 From: Jeremy Green Date: Sat, 16 Sep 2017 10:56:04 -0600 Subject: Update payload names for `sql.active_record` to be more descriptive. Fixes #30586. --- activerecord/CHANGELOG.md | 7 +++ activerecord/lib/active_record/relation.rb | 8 +-- activerecord/test/cases/instrumentation_test.rb | 72 +++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 activerecord/test/cases/instrumentation_test.rb diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index b421fedc96..55c4214eee 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Update payload names for `sql.active_record` instrumentation to be + more descriptive. + + Fixes #30586. + + *Jeremy Green* + * Add new error class `TransactionTimeout` for MySQL adapter which will be raised when lock wait time expires. diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 42a9d8492b..cf0d14f4e1 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -63,7 +63,7 @@ module ActiveRecord @klass.connection.insert( im, - "SQL", + "#{@klass} Create", primary_key || false, primary_key_value, nil, @@ -86,7 +86,7 @@ module ActiveRecord @klass.connection.update( um, - "SQL", + "#{@klass} Update", ) end @@ -373,7 +373,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - @klass.connection.update stmt, "SQL" + @klass.connection.update stmt, "#{@klass} Update All" end # Updates an object (or multiple objects) and saves it to the database, if validations pass. @@ -503,7 +503,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - affected = @klass.connection.delete(stmt, "SQL") + affected = @klass.connection.delete(stmt, "#{@klass} Destroy") reset affected diff --git a/activerecord/test/cases/instrumentation_test.rb b/activerecord/test/cases/instrumentation_test.rb new file mode 100644 index 0000000000..86ffe8b0d7 --- /dev/null +++ b/activerecord/test/cases/instrumentation_test.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "cases/helper" +require "models/book" + +module ActiveRecord + class InstrumentationTest < ActiveRecord::TestCase + def test_payload_name_on_load + Book.create(name: "test book") + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new *args + if event.payload[:sql].match "SELECT" + assert_equal "Book Load", event.payload[:name] + end + end + Book.first + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + + def test_payload_name_on_create + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new *args + if event.payload[:sql].match "INSERT" + assert_equal "Book Create", event.payload[:name] + end + end + Book.create(name: "test book") + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + + def test_payload_name_on_update + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new *args + if event.payload[:sql].match "UPDATE" + assert_equal "Book Update", event.payload[:name] + end + end + book = Book.create(name: "test book") + book.update_attribute(:name, "new name") + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + + def test_payload_name_on_update_all + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new *args + if event.payload[:sql].match "UPDATE" + assert_equal "Book Update All", event.payload[:name] + end + end + Book.create(name: "test book") + Book.update_all(name: "new name") + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + + def test_payload_name_on_destroy + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new *args + if event.payload[:sql].match "DELETE" + assert_equal "Book Destroy", event.payload[:name] + end + end + book = Book.create(name: "test book") + book.destroy + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + end +end -- cgit v1.2.3 From 9b917789fab6236eb9f785e92c1247feccd9bc40 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Thu, 21 Sep 2017 08:08:04 +0900 Subject: Update link in The Asset Pipeline guide [ci skip] --- guides/source/asset_pipeline.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 17ab9c7600..8bd1f91304 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -154,7 +154,7 @@ environments. You can enable or disable it in your configuration through the More reading: -* [Optimize caching](http://code.google.com/speed/page-speed/docs/caching.html) +* [Optimize caching](https://developers.google.com/speed/docs/insights/LeverageBrowserCaching) * [Revving Filenames: don't use querystring](http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/) -- cgit v1.2.3 From 4883a82251d498388a7186367d251f2185faf4c3 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 21 Sep 2017 13:33:43 +0900 Subject: Fix "warning: `*' interpreted as argument prefix" This fixes following warning: ``` /home/travis/build/rails/rails/activerecord/test/cases/instrumentation_test.rb:11: warning: `*' interpreted as argument prefix /home/travis/build/rails/rails/activerecord/test/cases/instrumentation_test.rb:23: warning: `*' interpreted as argument prefix /home/travis/build/rails/rails/activerecord/test/cases/instrumentation_test.rb:35: warning: `*' interpreted as argument prefix /home/travis/build/rails/rails/activerecord/test/cases/instrumentation_test.rb:48: warning: `*' interpreted as argument prefix /home/travis/build/rails/rails/activerecord/test/cases/instrumentation_test.rb:61: warning: `*' interpreted as argument prefix ``` --- activerecord/test/cases/instrumentation_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/activerecord/test/cases/instrumentation_test.rb b/activerecord/test/cases/instrumentation_test.rb index 86ffe8b0d7..e6e8468757 100644 --- a/activerecord/test/cases/instrumentation_test.rb +++ b/activerecord/test/cases/instrumentation_test.rb @@ -8,7 +8,7 @@ module ActiveRecord def test_payload_name_on_load Book.create(name: "test book") subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| - event = ActiveSupport::Notifications::Event.new *args + event = ActiveSupport::Notifications::Event.new(*args) if event.payload[:sql].match "SELECT" assert_equal "Book Load", event.payload[:name] end @@ -20,7 +20,7 @@ module ActiveRecord def test_payload_name_on_create subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| - event = ActiveSupport::Notifications::Event.new *args + event = ActiveSupport::Notifications::Event.new(*args) if event.payload[:sql].match "INSERT" assert_equal "Book Create", event.payload[:name] end @@ -32,7 +32,7 @@ module ActiveRecord def test_payload_name_on_update subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| - event = ActiveSupport::Notifications::Event.new *args + event = ActiveSupport::Notifications::Event.new(*args) if event.payload[:sql].match "UPDATE" assert_equal "Book Update", event.payload[:name] end @@ -45,7 +45,7 @@ module ActiveRecord def test_payload_name_on_update_all subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| - event = ActiveSupport::Notifications::Event.new *args + event = ActiveSupport::Notifications::Event.new(*args) if event.payload[:sql].match "UPDATE" assert_equal "Book Update All", event.payload[:name] end @@ -58,7 +58,7 @@ module ActiveRecord def test_payload_name_on_destroy subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| - event = ActiveSupport::Notifications::Event.new *args + event = ActiveSupport::Notifications::Event.new(*args) if event.payload[:sql].match "DELETE" assert_equal "Book Destroy", event.payload[:name] end -- cgit v1.2.3 From 611f2b2911197946bd81d5d5fb017e7133d77d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Emin=20=C4=B0NA=C3=87?= Date: Tue, 15 Mar 2016 03:01:09 +0200 Subject: Use algorithm while removing index with db:rollback Closes #24190 --- activerecord/CHANGELOG.md | 6 ++++++ activerecord/lib/active_record/migration/command_recorder.rb | 4 ++-- activerecord/test/cases/migration/command_recorder_test.rb | 5 +++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 55c4214eee..c34236d4be 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Use given algorithm while removing index from database. + + Fixes #24190. + + *Mehmet Emin İNAÇ* + * Update payload names for `sql.active_record` instrumentation to be more descriptive. diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index a3a5e0fa16..ac7d506fd1 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -161,8 +161,8 @@ module ActiveRecord table, columns, options = *args options ||= {} - index_name = options[:name] - options_hash = index_name ? { name: index_name } : { column: columns } + options_hash = options.slice(:name, :algorithm) + options_hash[:column] = columns if !options_hash[:name] [:remove_index, [table, options_hash]] end diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb index 0b5e983f14..58bc558619 100644 --- a/activerecord/test/cases/migration/command_recorder_test.rb +++ b/activerecord/test/cases/migration/command_recorder_test.rb @@ -213,6 +213,11 @@ module ActiveRecord assert_equal [:remove_index, [:table, { name: "new_index" }]], remove end + def test_invert_add_index_with_algorithm_option + remove = @recorder.inverse_of :add_index, [:table, :one, algorithm: :concurrently] + assert_equal [:remove_index, [:table, { column: :one, algorithm: :concurrently }]], remove + end + def test_invert_remove_index add = @recorder.inverse_of :remove_index, [:table, :one] assert_equal [:add_index, [:table, :one]], add -- cgit v1.2.3 From daa592293b5a10f487cc9000863e052d2a28884f Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 22 Sep 2017 10:29:23 +0900 Subject: Use `TOPLEVEL_BINDING` in rails runner command Binding to capture the local scope. This means that if a constant with same name as constant specified by the user exists in local scope, constant defined in local will use. This is different from what the user expects. Therefore, fixed to use top-level binding instead of local scope. Fixes #30644 --- railties/lib/rails/commands/runner/runner_command.rb | 4 ++-- railties/test/application/runner_test.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/commands/runner/runner_command.rb b/railties/lib/rails/commands/runner/runner_command.rb index cd9462e08f..30fbf04982 100644 --- a/railties/lib/rails/commands/runner/runner_command.rb +++ b/railties/lib/rails/commands/runner/runner_command.rb @@ -32,13 +32,13 @@ module Rails ARGV.replace(command_argv) if code_or_file == "-" - eval($stdin.read, binding, "stdin") + eval($stdin.read, TOPLEVEL_BINDING, "stdin") elsif File.exist?(code_or_file) $0 = code_or_file Kernel.load code_or_file else begin - eval(code_or_file, binding, __FILE__, __LINE__) + eval(code_or_file, TOPLEVEL_BINDING, __FILE__, __LINE__) rescue SyntaxError, NameError => error $stderr.puts "Please specify a valid ruby command or the path of a script to run." $stderr.puts "Run '#{self.class.executable} -h' for help." diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb index 64c46c4b45..aa5d495c97 100644 --- a/railties/test/application/runner_test.rb +++ b/railties/test/application/runner_test.rb @@ -128,5 +128,17 @@ module ApplicationTests assert_match "production", rails("runner", "puts Rails.env") end end + + def test_can_call_same_name_class_as_defined_in_thor + app_file "app/models/task.rb", <<-MODEL + class Task + def self.count + 42 + end + end + MODEL + + assert_match "42", rails("runner", "puts Task.count") + end end end -- cgit v1.2.3 From 72a22fa9299d41b6d088bb4a0c3c6807f5bed07d Mon Sep 17 00:00:00 2001 From: Alecs Popa Date: Thu, 21 Sep 2017 19:55:16 +0300 Subject: Implement change_table_comment and change_column_comment for MySql Adapter --- .../connection_adapters/abstract_mysql_adapter.rb | 10 ++++++++++ activerecord/test/cases/comment_test.rb | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) 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 7cd086084a..3dad43ced8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -311,6 +311,11 @@ module ActiveRecord execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}") end + def change_table_comment(table_name, comment) #:nodoc: + comment = "" if comment.nil? + execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}") + end + # Renames a table. # # Example: @@ -365,6 +370,11 @@ module ActiveRecord change_column table_name, column_name, column.sql_type, null: null end + def change_column_comment(table_name, column_name, comment) #:nodoc: + column = column_for(table_name, column_name) + change_column table_name, column_name, column.sql_type, comment: comment + end + def change_column(table_name, column_name, type, options = {}) #:nodoc: execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}") end diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb index 1bcafd4b55..40eb3e08c1 100644 --- a/activerecord/test/cases/comment_test.rb +++ b/activerecord/test/cases/comment_test.rb @@ -142,5 +142,27 @@ if ActiveRecord::Base.connection.supports_comments? assert_match %r[t\.string\s+"absent_comment"\n], output assert_no_match %r[t\.string\s+"absent_comment", comment:\n], output end + + def test_change_table_comment + @connection.change_table_comment :commenteds, "Edited table comment" + assert_equal "Edited table comment", @connection.table_comment("commenteds") + end + + def test_change_table_comment_to_nil + @connection.change_table_comment :commenteds, nil + assert @connection.table_comment("commenteds").blank? + end + + def test_change_column_comment + @connection.change_column_comment :commenteds, :name, "Edited column comment" + column = Commented.columns_hash["name"] + assert_equal "Edited column comment", column.comment + end + + def test_change_column_comment_to_nil + @connection.change_column_comment :commenteds, :name, nil + column = Commented.columns_hash["name"] + assert_nil column.comment + end end end -- cgit v1.2.3 From 6fd81d2d8ee77053c10a998c77d3799dfd3c51c0 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 22 Sep 2017 23:04:54 +0900 Subject: Return nil if table comment is blank --- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 2 +- activerecord/test/cases/comment_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 3dad43ced8..6e35bb0bbe 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -284,7 +284,7 @@ module ActiveRecord def table_comment(table_name) # :nodoc: scope = quoted_scope(table_name) - query_value(<<-SQL.strip_heredoc, "SCHEMA") + query_value(<<-SQL.strip_heredoc, "SCHEMA").presence SELECT table_comment FROM information_schema.tables WHERE table_schema = #{scope[:schema]} diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb index 40eb3e08c1..584e03d196 100644 --- a/activerecord/test/cases/comment_test.rb +++ b/activerecord/test/cases/comment_test.rb @@ -150,7 +150,7 @@ if ActiveRecord::Base.connection.supports_comments? def test_change_table_comment_to_nil @connection.change_table_comment :commenteds, nil - assert @connection.table_comment("commenteds").blank? + assert_nil @connection.table_comment("commenteds") end def test_change_column_comment -- cgit v1.2.3 From 18346bd52ab84fca996f4dfc1dd110ab2edf1f46 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 22 Sep 2017 23:09:53 +0900 Subject: `index_name` should be quoted --- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6e35bb0bbe..46ab8f3edd 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -726,7 +726,7 @@ module ActiveRecord def remove_index_sql(table_name, options = {}) index_name = index_name_for_remove(table_name, options) - "DROP INDEX #{index_name}" + "DROP INDEX #{quote_column_name(index_name)}" end def add_timestamps_sql(table_name, options = {}) -- cgit v1.2.3 From b798c5b796f58a9286e1a037db4453750cd26f95 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 23 Sep 2017 05:55:48 +0900 Subject: Prevent extra `column_for` for `change_column_{default,null,comment}` `change_column_{default,null,comment}` in mysql2 adapter are passing `column.sql_type` as `type` to `change_column` to intend keeping previous type. But `column_for` requires extra query, so use passing `nil` to `type` explicitly in the internal for the purpose. --- .../connection_adapters/abstract_mysql_adapter.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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 46ab8f3edd..ae991d3d79 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -356,23 +356,19 @@ module ActiveRecord def change_column_default(table_name, column_name, default_or_changes) #:nodoc: default = extract_new_default_value(default_or_changes) - column = column_for(table_name, column_name) - change_column table_name, column_name, column.sql_type, default: default + change_column table_name, column_name, nil, default: default end def change_column_null(table_name, column_name, null, default = nil) #:nodoc: - column = column_for(table_name, column_name) - unless null || default.nil? execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") end - change_column table_name, column_name, column.sql_type, null: null + change_column table_name, column_name, nil, null: null end def change_column_comment(table_name, column_name, comment) #:nodoc: - column = column_for(table_name, column_name) - change_column table_name, column_name, column.sql_type, comment: comment + change_column table_name, column_name, nil, comment: comment end def change_column(table_name, column_name, type, options = {}) #:nodoc: @@ -678,6 +674,7 @@ module ActiveRecord def change_column_sql(table_name, column_name, type, options = {}) column = column_for(table_name, column_name) + type ||= column.sql_type unless options.key?(:default) options[:default] = column.default -- cgit v1.2.3 From cfc461c3f8ee3a5f35fb05c75e492176c4d8854a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 22 Sep 2017 21:33:14 -0400 Subject: Ensure `1 AS one` for SQL Server with calculations. --- activerecord/lib/active_record/relation/calculations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 42d43224fa..0889d61c92 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -391,7 +391,7 @@ module ActiveRecord def build_count_subquery(relation, column_name, distinct) relation.select_values = [ if column_name == :all - distinct ? table[Arel.star] : Arel.sql("1") + distinct ? table[Arel.star] : Arel.sql(FinderMethods::ONE_AS_ONE) else column_alias = Arel.sql("count_column") aggregate_column(column_name).as(column_alias) -- cgit v1.2.3 From dfe6939e16bdcf854d32b61933d55fe313ac49fb Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 23 Sep 2017 13:18:11 +0900 Subject: Adding legacy primary key should be compatible Currently implicit legacy primary key is compatible, but adding explicit legacy primary key is not compatible. It should also be fixed. Fixes #30664. --- .../lib/active_record/migration/compatibility.rb | 19 ++++++++ .../test/cases/migration/compatibility_test.rb | 51 ++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index 87c1c58aff..92993d64a7 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -20,6 +20,11 @@ module ActiveRecord class V5_0 < V5_1 module TableDefinition + def primary_key(name, type = :primary_key, **options) + type = :integer if type == :primary_key + super + end + def references(*args, **options) super(*args, type: :integer, **options) end @@ -86,6 +91,20 @@ module ActiveRecord end end + def add_column(table_name, column_name, type, options = {}) + if type == :primary_key + case adapter_name + when "PostgreSQL" + type = :serial + when "Mysql2" + type = :integer + options[:auto_increment] = true + end + options[:primary_key] = true + end + super + end + def add_reference(table_name, ref_name, **options) super(table_name, ref_name, type: :integer, **options) end diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index e1311c0f21..1ae15eb439 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -199,6 +199,57 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase assert_match %r{create_table "legacy_primary_keys", id: :integer, default: nil}, schema end + if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter) + def test_legacy_primary_key_in_create_table_should_be_integer + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_table :legacy_primary_keys, id: false do |t| + t.primary_key :id + end + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "legacy_primary_keys" + assert_match %r{create_table "legacy_primary_keys", id: :(?:integer|serial), (?!default: nil)}, schema + end + + def test_legacy_primary_key_in_change_table_should_be_integer + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_table :legacy_primary_keys, id: false do |t| + t.integer :dummy + end + change_table :legacy_primary_keys do |t| + t.primary_key :id + end + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "legacy_primary_keys" + assert_match %r{create_table "legacy_primary_keys", id: :(?:integer|serial), (?!default: nil)}, schema + end + + def test_add_column_with_legacy_primary_key_should_be_integer + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_table :legacy_primary_keys, id: false do |t| + t.integer :dummy + end + add_column :legacy_primary_keys, :id, :primary_key + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "legacy_primary_keys" + assert_match %r{create_table "legacy_primary_keys", id: :(?:integer|serial), (?!default: nil)}, schema + end + end + def test_legacy_join_table_foreign_keys_should_be_integer @migration = Class.new(ActiveRecord::Migration[5.0]) { def change -- cgit v1.2.3 From 202d6578f44ab03ee89ed57b73a97d513fc5a008 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 23 Sep 2017 15:34:56 +0900 Subject: Move integer-like primary key normalization to `new_column_definition` Currently the normalization only exists in `primary_key` shorthand. It should be moved to `new_column_definition` to also affect to `add_column` with primary key. --- .../abstract/schema_definitions.rb | 4 ++++ .../mysql/schema_definitions.rb | 9 ++++----- .../postgresql/schema_definitions.rb | 19 ++++++++++++------- .../sqlite3/schema_definitions.rb | 22 +++++++--------------- .../sqlite3/schema_statements.rb | 4 ---- .../lib/active_record/migration/compatibility.rb | 8 +------- 6 files changed, 28 insertions(+), 38 deletions(-) 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 788a455773..1041db0b8f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -410,6 +410,10 @@ module ActiveRecord def aliased_types(name, fallback) "timestamp" == name ? :datetime : fallback end + + def integer_like_primary_key?(type, options) + options[:primary_key] && [:integer, :bigint].include?(type) && !options.key?(:default) + end end class AlterTable # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb index b22a2e4da7..eff96af87a 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb @@ -4,11 +4,6 @@ module ActiveRecord module ConnectionAdapters module MySQL module ColumnMethods - def primary_key(name, type = :primary_key, **options) - options[:auto_increment] = true if [:integer, :bigint].include?(type) && !options.key?(:default) - super - end - def blob(*args, **options) args.each { |name| column(name, :blob, options) } end @@ -62,6 +57,10 @@ module ActiveRecord include ColumnMethods def new_column_definition(name, type, **options) # :nodoc: + if integer_like_primary_key?(type, options) + options[:auto_increment] = true + end + case type when :virtual type = options[:type] diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb index f1489e4d69..cb13f9fec1 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb @@ -44,15 +44,8 @@ module ActiveRecord # a record (as primary keys cannot be +nil+). This might be done via the # +SecureRandom.uuid+ method and a +before_save+ callback, for instance. def primary_key(name, type = :primary_key, **options) - options[:auto_increment] = true if [:integer, :bigint].include?(type) && !options.key?(:default) if type == :uuid options[:default] = options.fetch(:default, "gen_random_uuid()") - elsif options.delete(:auto_increment) == true && %i(integer bigint).include?(type) - type = if type == :bigint || options[:limit] == 8 - :bigserial - else - :serial - end end super @@ -185,6 +178,18 @@ module ActiveRecord class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition include ColumnMethods + + def new_column_definition(name, type, **options) # :nodoc: + if integer_like_primary_key?(type, options) + type = if type == :bigint || options[:limit] == 8 + :bigserial + else + :serial + end + end + + super + end end class Table < ActiveRecord::ConnectionAdapters::Table diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb index 501f17dbad..2010de1ce2 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb @@ -3,27 +3,19 @@ module ActiveRecord module ConnectionAdapters module SQLite3 - module ColumnMethods - def primary_key(name, type = :primary_key, **options) - if %i(integer bigint).include?(type) && (options.delete(:auto_increment) == true || !options.key?(:default)) - type = :primary_key - end - - super - end - end - class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition - include ColumnMethods - def references(*args, **options) super(*args, type: :integer, **options) end alias :belongs_to :references - end - class Table < ActiveRecord::ConnectionAdapters::Table - include ColumnMethods + def new_column_definition(name, type, **options) # :nodoc: + if integer_like_primary_key?(type, options) + type = :primary_key + end + + super + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb index a512702b7b..f4e55147df 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb @@ -39,10 +39,6 @@ module ActiveRecord end end - def update_table_definition(table_name, base) - SQLite3::Table.new(table_name, base) - end - def create_schema_dumper(options) SQLite3::SchemaDumper.create(self, options) end diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index 92993d64a7..502cef2e20 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -93,13 +93,7 @@ module ActiveRecord def add_column(table_name, column_name, type, options = {}) if type == :primary_key - case adapter_name - when "PostgreSQL" - type = :serial - when "Mysql2" - type = :integer - options[:auto_increment] = true - end + type = :integer options[:primary_key] = true end super -- cgit v1.2.3 From 962ce60ff1692f6f2ed1a8322319d73b9790d324 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 23 Sep 2017 18:25:16 +0900 Subject: Make bang version work with `InheritableOptions` Currently, bang version does not work with `InheritableOptions`. `InheritableOptions` treats the argument Hash as the default value. However, `Hash#fetch` does not use the default value when key is not found, so can not get the default value. So in bang version, should use `Hash#[]` instead of `Hash#fetch`. --- activesupport/lib/active_support/ordered_options.rb | 2 +- activesupport/test/ordered_options_test.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index c2a37fbdd7..b74510fdb2 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -46,7 +46,7 @@ module ActiveSupport bangs = name_string.chomp!("!") if bangs - fetch(name_string.to_sym).presence || raise(KeyError.new(":#{name_string} is blank")) + self[name_string].presence || raise(KeyError.new(":#{name_string} is blank")) else self[name_string] end diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb index 7f2e774c02..2c67bb02ac 100644 --- a/activesupport/test/ordered_options_test.rb +++ b/activesupport/test/ordered_options_test.rb @@ -102,4 +102,17 @@ class OrderedOptionsTest < ActiveSupport::TestCase end assert_raises(KeyError) { a.non_existing_key! } end + + def test_inheritable_options_with_bang + a = ActiveSupport::InheritableOptions.new(foo: :bar) + + assert_nothing_raised { a.foo! } + assert_equal a.foo, a.foo! + + assert_raises(KeyError) do + a.foo = nil + a.foo! + end + assert_raises(KeyError) { a.non_existing_key! } + end end -- cgit v1.2.3 From 6b12d02e1586332503e93b8422d4d78286041e7e Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Thu, 21 Sep 2017 07:18:11 +0900 Subject: Fix rails middleware list in api_app guide [ci skip] * `MyApi::Application::Routes` is not middleware. --- guides/source/api_app.md | 1 - 1 file changed, 1 deletion(-) diff --git a/guides/source/api_app.md b/guides/source/api_app.md index da1b7b25ef..43a7de88b0 100644 --- a/guides/source/api_app.md +++ b/guides/source/api_app.md @@ -216,7 +216,6 @@ An API application comes with the following middleware by default: - `Rack::Head` - `Rack::ConditionalGet` - `Rack::ETag` -- `MyApi::Application::Routes` See the [internal middleware](rails_on_rack.html#internal-middleware-stack) section of the Rack guide for further information on them. -- cgit v1.2.3 From 39f8ca64cec8667b66628e970211b4d18abbc373 Mon Sep 17 00:00:00 2001 From: Michael Coyne Date: Sat, 23 Sep 2017 17:16:21 -0400 Subject: Add key rotation message Encryptor and Verifier Both classes now have a rotate method where new instances are added for each call. When decryption or verification fails the next rotation instance is tried. --- activesupport/CHANGELOG.md | 11 ++ .../lib/active_support/message_encryptor.rb | 32 ++++- .../lib/active_support/message_verifier.rb | 36 +++++- .../messages/rotation_configuration.rb | 25 ++++ .../lib/active_support/messages/rotator.rb | 81 ++++++++++++ activesupport/test/message_encryptor_test.rb | 118 +++++++++++++++++ activesupport/test/message_verifier_test.rb | 90 +++++++++++++ .../test/messages/rotation_configuration_test.rb | 43 +++++++ railties/lib/rails/application.rb | 5 +- .../test/application/middleware/cookies_test.rb | 143 +++++++++++++++++++++ 10 files changed, 579 insertions(+), 5 deletions(-) create mode 100644 activesupport/lib/active_support/messages/rotation_configuration.rb create mode 100644 activesupport/lib/active_support/messages/rotator.rb create mode 100644 activesupport/test/messages/rotation_configuration_test.rb diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 56013c5f95..487984cbd3 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,14 @@ +* Add key rotation support to `MessageEncryptor` and `MessageVerifier` + + This change introduces a `rotate` method to both the `MessageEncryptor` and + `MessageVerifier` classes. This method accepts the same arguments and + options as the given classes' constructor. The `encrypt_and_verify` method + for `MessageEncryptor` and the `verified` method for `MessageVerifier` also + accept an optional keyword argument `:on_rotation` block which is called + when a rotated instance is used to decrypt or verify the message. + + *Michael J Coyne* + * Deprecate `Module#reachable?` method. *bogdanvlviv* diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 27620f56be..003fb4c354 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -54,7 +54,37 @@ module ActiveSupport # # Then the messages can be verified and returned upto the expire time. # Thereafter, verifying returns +nil+. + # + # === Rotating keys + # + # This class also defines a +rotate+ method which can be used to rotate out + # encryption keys no longer in use. + # + # This method is called with an options hash where a +:cipher+ option and + # either a +:raw_key+ or +:secret+ option must be defined. If +:raw_key+ is + # defined, it is used directly for the underlying encryption function. If + # the +:secret+ option is defined, a +:salt+ option must also be defined and + # a +KeyGenerator+ instance will be used to derive a key using +:salt+. When + # +:secret+ is used, a +:key_generator+ option may also be defined allowing + # for custom +KeyGenerator+ instances. If CBC encryption is used a + # `:raw_signed_key` or a `:signed_salt` option must also be defined. A + # +:digest+ may also be defined when using CBC encryption. This method can be + # called multiple times and new encryptor instances will be added to the + # rotation stack on each call. + # + # # Specifying the key used for encryption + # crypt.rotate raw_key: old_aead_key, cipher: "aes-256-gcm" + # crypt.rotate raw_key: old_cbc_key, raw_signed_key: old_cbc_sign_key, cipher: "aes-256-cbc", digest: "SHA1" + # + # # Using a KeyGenerator instance with a secret and salt(s) + # crypt.rotate secret: old_aead_secret, salt: old_aead_salt, cipher: "aes-256-gcm" + # crypt.rotate secret: old_cbc_secret, salt: old_cbc_salt, signed_salt: old_cbc_signed_salt, cipher: "aes-256-cbc", digest: "SHA1" + # + # # Specifying the key generator instance + # crypt.rotate key_generator: old_key_gen, salt: old_salt, cipher: "aes-256-gcm" class MessageEncryptor + prepend Messages::Rotator::Encryptor + class << self attr_accessor :use_authenticated_message_encryption #:nodoc: @@ -126,7 +156,7 @@ module ActiveSupport # Decrypt and verify a message. We need to verify the message in order to # avoid padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/. - def decrypt_and_verify(data, purpose: nil) + def decrypt_and_verify(data, purpose: nil, **) _decrypt(verifier.verify(data), purpose) end diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 7110d6d2c9..0be13f6f03 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -4,6 +4,7 @@ require "base64" require_relative "core_ext/object/blank" require_relative "security_utils" require_relative "messages/metadata" +require_relative "messages/rotator" module ActiveSupport # +MessageVerifier+ makes it easy to generate and verify messages which are @@ -73,7 +74,36 @@ module ActiveSupport # Then the messages can be verified and returned upto the expire time. # Thereafter, the +verified+ method returns +nil+ while +verify+ raises # ActiveSupport::MessageVerifier::InvalidSignature. + # + # === Rotating keys + # + # This class also defines a +rotate+ method which can be used to rotate out + # verification keys no longer in use. + # + # This method is called with an options hash where a +:digest+ option and + # either a +:raw_key+ or +:secret+ option must be defined. If +:raw_key+ is + # defined, it is used directly for the underlying HMAC function. If the + # +:secret+ option is defined, a +:salt+ option must also be defined and a + # +KeyGenerator+ instance will be used to derive a key using +:salt+. When + # +:secret+ is used, a +:key_generator+ option may also be defined allowing + # for custom +KeyGenerator+ instances. This method can be called multiple + # times and new verifier instances will be added to the rotation stack on + # each call. + # + # # Specifying the key used for verification + # @verifier.rotate raw_key: older_key, digest: "SHA1" + # + # # Specify the digest + # @verifier.rotate raw_key: old_key, digest: "SHA256" + # + # # Using a KeyGenerator instance with a secret and salt + # @verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1" + # + # # Specifying the key generator instance + # @verifier.rotate key_generator: old_key_gen, salt: old_salt, digest: "SHA256" class MessageVerifier + prepend Messages::Rotator::Verifier + class InvalidSignature < StandardError; end def initialize(secret, options = {}) @@ -120,7 +150,7 @@ module ActiveSupport # # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff" # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format - def verified(signed_message, purpose: nil) + def verified(signed_message, purpose: nil, **) if valid_message?(signed_message) begin data = signed_message.split("--".freeze)[0] @@ -145,8 +175,8 @@ module ActiveSupport # # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit' # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature - def verify(signed_message, purpose: nil) - verified(signed_message, purpose: purpose) || raise(InvalidSignature) + def verify(*args) + verified(*args) || raise(InvalidSignature) end # Generates a signed message for the provided value. diff --git a/activesupport/lib/active_support/messages/rotation_configuration.rb b/activesupport/lib/active_support/messages/rotation_configuration.rb new file mode 100644 index 0000000000..12566bdb63 --- /dev/null +++ b/activesupport/lib/active_support/messages/rotation_configuration.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module ActiveSupport + module Messages + class RotationConfiguration + attr_reader :signed, :encrypted + + def initialize + @signed, @encrypted = [], [] + end + + def rotate(kind = nil, **options) + case kind + when :signed + @signed << options + when :encrypted + @encrypted << options + else + rotate :signed, options + rotate :encrypted, options + end + end + end + end +end diff --git a/activesupport/lib/active_support/messages/rotator.rb b/activesupport/lib/active_support/messages/rotator.rb new file mode 100644 index 0000000000..21ae643138 --- /dev/null +++ b/activesupport/lib/active_support/messages/rotator.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +module ActiveSupport + module Messages + module Rotator # :nodoc: + def initialize(*args) + super + + @rotations = [] + end + + def rotate(*args) + @rotations << create_rotation(*args) + end + + module Encryptor + include Rotator + + def decrypt_and_verify(*args, on_rotation: nil, **options) + super + rescue MessageEncryptor::InvalidMessage, MessageVerifier::InvalidSignature + run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, options) } || raise + end + + private + def create_rotation(raw_key: nil, raw_signed_key: nil, **options) + self.class.new \ + raw_key || extract_key(options), + raw_signed_key || extract_signing_key(options), + options.slice(:cipher, :digest, :serializer) + end + + def extract_key(cipher:, salt:, key_generator: nil, secret: nil, **) + key_generator ||= key_generator_for(secret) + key_generator.generate_key(salt, self.class.key_len(cipher)) + end + + def extract_signing_key(cipher:, signed_salt: nil, key_generator: nil, secret: nil, **) + if cipher.downcase.end_with?("cbc") + raise ArgumentError, "missing signed_salt for signing key generation" unless signed_salt + + key_generator ||= key_generator_for(secret) + key_generator.generate_key(signed_salt) + end + end + end + + module Verifier + include Rotator + + def verified(*args, on_rotation: nil, **options) + super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, options) } + end + + private + def create_rotation(raw_key: nil, digest: nil, serializer: nil, **options) + self.class.new(raw_key || extract_key(options), digest: digest, serializer: serializer) + end + + def extract_key(key_generator: nil, secret: nil, salt:) + key_generator ||= key_generator_for(secret) + key_generator.generate_key(salt) + end + end + + private + def key_generator_for(secret) + ActiveSupport::KeyGenerator.new(secret, iterations: 1000) + end + + def run_rotations(on_rotation) + @rotations.find do |rotation| + if message = yield(rotation) rescue next + on_rotation.call if on_rotation + return message + end + end + end + end + end +end diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index 1fbe655642..17baf3550b 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -115,6 +115,124 @@ class MessageEncryptorTest < ActiveSupport::TestCase assert_equal "Ruby on Rails", encryptor.decrypt_and_verify(encrypted_message) end + def test_with_rotated_raw_key + old_raw_key = SecureRandom.random_bytes(32) + old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm") + old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw key") + + encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") + encryptor.rotate raw_key: old_raw_key, cipher: "aes-256-gcm" + + assert_equal "message encrypted with old raw key", encryptor.decrypt_and_verify(old_message) + end + + def test_with_rotated_secret_and_salt + old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" + old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt, 32) + + old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm") + old_message = old_encryptor.encrypt_and_sign("message encrypted with old secret and salt") + + encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") + encryptor.rotate secret: old_secret, salt: old_salt, cipher: "aes-256-gcm" + + assert_equal "message encrypted with old secret and salt", encryptor.decrypt_and_verify(old_message) + end + + def test_with_rotated_key_generator + old_key_gen, old_salt = ActiveSupport::KeyGenerator.new(SecureRandom.random_bytes(32), iterations: 256), "old salt" + + old_raw_key = old_key_gen.generate_key(old_salt, 32) + old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm") + old_message = old_encryptor.encrypt_and_sign("message encrypted with old key generator and salt") + + encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") + encryptor.rotate key_generator: old_key_gen, salt: old_salt, cipher: "aes-256-gcm" + + assert_equal "message encrypted with old key generator and salt", encryptor.decrypt_and_verify(old_message) + end + + def test_with_rotated_aes_cbc_encryptor_with_raw_keys + old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) + + old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") + old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw keys") + + encryptor = ActiveSupport::MessageEncryptor.new(@secret) + encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" + + assert_equal "message encrypted with old raw keys", encryptor.decrypt_and_verify(old_message) + end + + def test_with_rotated_aes_cbc_encryptor_with_secret_and_salts + old_secret, old_salt, old_signed_salt = SecureRandom.random_bytes(32), "old salt", "old signed salt" + + old_key_gen = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000) + old_raw_key = old_key_gen.generate_key(old_salt, 32) + old_raw_signed_key = old_key_gen.generate_key(old_signed_salt) + + old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") + old_message = old_encryptor.encrypt_and_sign("message encrypted with old secret and salts") + + encryptor = ActiveSupport::MessageEncryptor.new(@secret) + encryptor.rotate secret: old_secret, salt: old_salt, signed_salt: old_signed_salt, cipher: "aes-256-cbc", digest: "SHA1" + + assert_equal "message encrypted with old secret and salts", encryptor.decrypt_and_verify(old_message) + end + + def test_with_rotating_multiple_encryptors + older_raw_key, older_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) + old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) + + older_encryptor = ActiveSupport::MessageEncryptor.new(older_raw_key, older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") + older_message = older_encryptor.encrypt_and_sign("message encrypted with older raw key") + + old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") + old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw key") + + encryptor = ActiveSupport::MessageEncryptor.new(@secret) + encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" + encryptor.rotate raw_key: older_raw_key, raw_signed_key: older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" + + assert_equal "encrypted message", encryptor.decrypt_and_verify(encryptor.encrypt_and_sign("encrypted message")) + assert_equal "message encrypted with old raw key", encryptor.decrypt_and_verify(old_message) + assert_equal "message encrypted with older raw key", encryptor.decrypt_and_verify(older_message) + end + + def test_on_rotation_instance_callback_is_called_and_returns_modified_messages + callback_ran, message = nil, nil + + older_raw_key, older_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) + old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) + + older_encryptor = ActiveSupport::MessageEncryptor.new(older_raw_key, older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") + older_message = older_encryptor.encrypt_and_sign(encoded: "message") + + encryptor = ActiveSupport::MessageEncryptor.new(@secret) + encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" + encryptor.rotate raw_key: older_raw_key, raw_signed_key: older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" + + message = encryptor.decrypt_and_verify(older_message, on_rotation: proc { callback_ran = true }) + + assert callback_ran, "callback was ran" + assert_equal({ encoded: "message" }, message) + end + + def test_with_rotated_metadata + old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" + old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt, 32) + + old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm") + old_message = old_encryptor.encrypt_and_sign( + "message encrypted with old secret, salt, and metadata", purpose: "rotation") + + encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") + encryptor.rotate secret: old_secret, salt: old_salt, cipher: "aes-256-gcm" + + assert_equal "message encrypted with old secret, salt, and metadata", + encryptor.decrypt_and_verify(old_message, purpose: "rotation") + end + private def assert_aead_not_decrypted(encryptor, value) assert_raise(ActiveSupport::MessageEncryptor::InvalidMessage) do diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index fbeafca203..3079c48c02 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -20,6 +20,7 @@ class MessageVerifierTest < ActiveSupport::TestCase def setup @verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!") @data = { some: "data", now: Time.utc(2010) } + @secret = SecureRandom.random_bytes(32) end def test_valid_message @@ -90,6 +91,95 @@ class MessageVerifierTest < ActiveSupport::TestCase signed_message = "BAh7BzoJc29tZUkiCWRhdGEGOgZFVDoIbm93SXU6CVRpbWUNIIAbgAAAAAAHOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7BkY=--d03c52c91dfe4ccc5159417c660461bcce005e96" assert_equal @data, @verifier.verify(signed_message) end + + def test_with_rotated_raw_key + old_raw_key = SecureRandom.random_bytes(32) + + old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1") + old_message = old_verifier.generate("message verified with old raw key") + + verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") + verifier.rotate raw_key: old_raw_key, digest: "SHA1" + + assert_equal "message verified with old raw key", verifier.verified(old_message) + end + + def test_with_rotated_secret_and_salt + old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" + + old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt) + old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1") + old_message = old_verifier.generate("message verified with old secret and salt") + + verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") + verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1" + + assert_equal "message verified with old secret and salt", verifier.verified(old_message) + end + + def test_with_rotated_key_generator + old_key_gen, old_salt = ActiveSupport::KeyGenerator.new(SecureRandom.random_bytes(32), iterations: 256), "old salt" + + old_raw_key = old_key_gen.generate_key(old_salt) + old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1") + old_message = old_verifier.generate("message verified with old key generator and salt") + + verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") + verifier.rotate key_generator: old_key_gen, salt: old_salt, digest: "SHA1" + + assert_equal "message verified with old key generator and salt", verifier.verified(old_message) + end + + def test_with_rotating_multiple_verifiers + old_raw_key, older_raw_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(32) + + old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA256") + old_message = old_verifier.generate("message verified with old raw key") + + older_verifier = ActiveSupport::MessageVerifier.new(older_raw_key, digest: "SHA1") + older_message = older_verifier.generate("message verified with older raw key") + + verifier = ActiveSupport::MessageVerifier.new("new secret", digest: "SHA512") + verifier.rotate raw_key: old_raw_key, digest: "SHA256" + verifier.rotate raw_key: older_raw_key, digest: "SHA1" + + assert_equal "verified message", verifier.verified(verifier.generate("verified message")) + assert_equal "message verified with old raw key", verifier.verified(old_message) + assert_equal "message verified with older raw key", verifier.verified(older_message) + end + + def test_on_rotation_keyword_block_is_called_and_verified_returns_message + callback_ran, message = nil, nil + + old_raw_key, older_raw_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(32) + + older_verifier = ActiveSupport::MessageVerifier.new(older_raw_key, digest: "SHA1") + older_message = older_verifier.generate(encoded: "message") + + verifier = ActiveSupport::MessageVerifier.new("new secret", digest: "SHA512") + verifier.rotate raw_key: old_raw_key, digest: "SHA256" + verifier.rotate raw_key: older_raw_key, digest: "SHA1" + + message = verifier.verified(older_message, on_rotation: proc { callback_ran = true }) + + assert callback_ran, "callback was ran" + assert_equal({ encoded: "message" }, message) + end + + def test_with_rotated_metadata + old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" + + old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt) + old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1") + old_message = old_verifier.generate( + "message verified with old secret, salt, and metadata", purpose: "rotation") + + verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") + verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1" + + assert_equal "message verified with old secret, salt, and metadata", + verifier.verified(old_message, purpose: "rotation") + end end class MessageVerifierMetadataTest < ActiveSupport::TestCase diff --git a/activesupport/test/messages/rotation_configuration_test.rb b/activesupport/test/messages/rotation_configuration_test.rb new file mode 100644 index 0000000000..41d938e119 --- /dev/null +++ b/activesupport/test/messages/rotation_configuration_test.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "active_support/messages/rotation_configuration" + +class MessagesRotationConfiguration < ActiveSupport::TestCase + def setup + @config = ActiveSupport::Messages::RotationConfiguration.new + end + + def test_signed_configurations + @config.rotate :signed, secret: "older secret", salt: "salt", digest: "SHA1" + @config.rotate :signed, secret: "old secret", salt: "salt", digest: "SHA256" + + assert_equal [{ + secret: "older secret", salt: "salt", digest: "SHA1" + }, { + secret: "old secret", salt: "salt", digest: "SHA256" + }], @config.signed + end + + def test_encrypted_configurations + @config.rotate :encrypted, raw_key: "old raw key", cipher: "aes-256-gcm" + + assert_equal [{ + raw_key: "old raw key", cipher: "aes-256-gcm" + }], @config.encrypted + end + + def test_rotate_without_kind + @config.rotate secret: "older secret", salt: "salt", digest: "SHA1" + @config.rotate raw_key: "old raw key", cipher: "aes-256-gcm" + + expected = [{ + secret: "older secret", salt: "salt", digest: "SHA1" + }, { + raw_key: "old raw key", cipher: "aes-256-gcm" + }] + + assert_equal expected, @config.encrypted + assert_equal expected, @config.signed + end +end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index abfec90b6d..f691156921 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -259,8 +259,11 @@ module Rails "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt, "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt, "action_dispatch.authenticated_encrypted_cookie_salt" => config.action_dispatch.authenticated_encrypted_cookie_salt, + "action_dispatch.encrypted_cookie_cipher" => config.action_dispatch.encrypted_cookie_cipher, + "action_dispatch.signed_cookie_digest" => config.action_dispatch.signed_cookie_digest, "action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer, - "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest + "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest, + "action_dispatch.cookies_rotations" => config.action_dispatch.cookies_rotations ) end end diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb index 23f1ec3e35..932a6d0e77 100644 --- a/railties/test/application/middleware/cookies_test.rb +++ b/railties/test/application/middleware/cookies_test.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true require "isolation/abstract_unit" +require "rack/test" module ApplicationTests class CookiesTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation + include Rack::Test::Methods def new_app File.expand_path("#{app_path}/../new_app") @@ -15,6 +17,10 @@ module ApplicationTests FileUtils.rm_rf("#{app_path}/config/environments") end + def app + Rails.application + end + def teardown teardown_app FileUtils.rm_rf(new_app) if File.directory?(new_app) @@ -44,5 +50,142 @@ module ApplicationTests require "#{app_path}/config/environment" assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie end + + test "signed cookies with SHA512 digest and rotated out SHA256 and SHA1 digests" do + key_gen_sha1 = ActiveSupport::KeyGenerator.new("legacy sha1 secret", iterations: 1000) + key_gen_sha256 = ActiveSupport::KeyGenerator.new("legacy sha256 secret", iterations: 1000) + + verifer_sha1 = ActiveSupport::MessageVerifier.new(key_gen_sha1.generate_key("sha1 salt"), digest: :SHA1) + verifer_sha256 = ActiveSupport::MessageVerifier.new(key_gen_sha256.generate_key("sha256 salt"), digest: :SHA256) + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get ':controller(/:action)' + post ':controller(/:action)' + end + RUBY + + controller :foo, <<-RUBY + class FooController < ActionController::Base + protect_from_forgery with: :null_session + + def write_raw_cookie_sha1 + cookies[:signed_cookie] = "#{verifer_sha1.generate("signed cookie")}" + head :ok + end + + def write_raw_cookie_sha256 + cookies[:signed_cookie] = "#{verifer_sha256.generate("signed cookie")}" + head :ok + end + + def read_signed + render plain: cookies.signed[:signed_cookie].inspect + end + + def read_raw_cookie + render plain: cookies[:signed_cookie] + end + end + RUBY + + add_to_config <<-RUBY + config.action_dispatch.cookies_rotations.rotate :signed, + digest: "SHA1", secret: "legacy sha1 secret", salt: "sha1 salt" + + config.action_dispatch.cookies_rotations.rotate :signed, + digest: "SHA256", secret: "legacy sha256 secret", salt: "sha256 salt" + + config.action_dispatch.signed_cookie_digest = "SHA512" + config.action_dispatch.signed_cookie_salt = "sha512 salt" + RUBY + + require "#{app_path}/config/environment" + + verifer_sha512 = ActiveSupport::MessageVerifier.new(app.key_generator.generate_key("sha512 salt"), digest: :SHA512) + + get "/foo/write_raw_cookie_sha1" + get "/foo/read_signed" + assert_equal "signed cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "signed cookie", verifer_sha512.verify(last_response.body) + + get "/foo/write_raw_cookie_sha256" + get "/foo/read_signed" + assert_equal "signed cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "signed cookie", verifer_sha512.verify(last_response.body) + end + + test "encrypted cookies with multiple rotated out ciphers" do + key_gen_one = ActiveSupport::KeyGenerator.new("legacy secret one", iterations: 1000) + key_gen_two = ActiveSupport::KeyGenerator.new("legacy secret two", iterations: 1000) + + encryptor_one = ActiveSupport::MessageEncryptor.new(key_gen_one.generate_key("salt one", 32), cipher: "aes-256-gcm") + encryptor_two = ActiveSupport::MessageEncryptor.new(key_gen_two.generate_key("salt two", 32), cipher: "aes-256-gcm") + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get ':controller(/:action)' + post ':controller(/:action)' + end + RUBY + + controller :foo, <<-RUBY + class FooController < ActionController::Base + protect_from_forgery with: :null_session + + def write_raw_cookie_one + cookies[:encrypted_cookie] = "#{encryptor_one.encrypt_and_sign("encrypted cookie")}" + head :ok + end + + def write_raw_cookie_two + cookies[:encrypted_cookie] = "#{encryptor_two.encrypt_and_sign("encrypted cookie")}" + head :ok + end + + def read_encrypted + render plain: cookies.encrypted[:encrypted_cookie].inspect + end + + def read_raw_cookie + render plain: cookies[:encrypted_cookie] + end + end + RUBY + + add_to_config <<-RUBY + config.action_dispatch.use_authenticated_cookie_encryption = true + config.action_dispatch.encrypted_cookie_cipher = "aes-256-gcm" + config.action_dispatch.authenticated_encrypted_cookie_salt = "salt" + + config.action_dispatch.cookies_rotations.rotate :encrypted, + cipher: "aes-256-gcm", secret: "legacy secret one", salt: "salt one" + + config.action_dispatch.cookies_rotations.rotate :encrypted, + cipher: "aes-256-gcm", secret: "legacy secret two", salt: "salt two" + RUBY + + require "#{app_path}/config/environment" + + encryptor = ActiveSupport::MessageEncryptor.new(app.key_generator.generate_key("salt", 32), cipher: "aes-256-gcm") + + get "/foo/write_raw_cookie_one" + get "/foo/read_encrypted" + assert_equal "encrypted cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body) + + get "/foo/write_raw_cookie_sha256" + get "/foo/read_encrypted" + assert_equal "encrypted cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body) + end end end -- cgit v1.2.3 From 50bc497a9fd38188ed45e275492f149a5b63a3ee Mon Sep 17 00:00:00 2001 From: Jaime Alvarez Date: Wed, 13 Sep 2017 17:17:00 +0000 Subject: Update Action Mailer Basics [skip ci] --- guides/source/action_mailer_basics.md | 77 ++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index 96ef9c4450..a025fb773a 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -92,8 +92,8 @@ registered email address: class UserMailer < ApplicationMailer default from: 'notifications@example.com' - def welcome_email(user) - @user = user + def welcome_email + @user = params[:user] @url = 'http://example.com/login' mail(to: @user.email, subject: 'Welcome to My Awesome Site') end @@ -191,7 +191,7 @@ class UsersController < ApplicationController respond_to do |format| if @user.save # Tell the UserMailer to send a welcome email after save - UserMailer.welcome_email(@user).deliver_later + UserMailer.welcome_email.with(user: @user).deliver_later format.html { redirect_to(@user, notice: 'User was successfully created.') } format.json { render json: @user, status: :created, location: @user } @@ -220,7 +220,7 @@ If you want to send emails right away (from a cronjob for example) just call class SendWeeklySummary def run User.find_each do |user| - UserMailer.weekly_summary(user).deliver_now + UserMailer.with(user: user).weekly_summary.deliver_now end end end @@ -331,7 +331,7 @@ with the addresses separated by commas. ```ruby class AdminMailer < ApplicationMailer - default to: Proc.new { Admin.pluck(:email) }, + default to: -> { Admin.pluck(:email) }, from: 'notification@example.com' def new_registration(user) @@ -351,8 +351,8 @@ address when they receive the email. The trick to doing that is to format the email address in the format `"Full Name" `. ```ruby -def welcome_email(user) - @user = user +def welcome_email + @user = params[:user] email_with_name = %("#{@user.name}" <#{@user.email}>) mail(to: email_with_name, subject: 'Welcome to My Awesome Site') end @@ -372,8 +372,8 @@ To change the default mailer view for your action you do something like: class UserMailer < ApplicationMailer default from: 'notifications@example.com' - def welcome_email(user) - @user = user + def welcome_email + @user = params[:user] @url = 'http://example.com/login' mail(to: @user.email, subject: 'Welcome to My Awesome Site', @@ -394,8 +394,8 @@ templates or even render inline or text without using a template file: class UserMailer < ApplicationMailer default from: 'notifications@example.com' - def welcome_email(user) - @user = user + def welcome_email + @user = params[:user] @url = 'http://example.com/login' mail(to: @user.email, subject: 'Welcome to My Awesome Site') do |format| @@ -453,8 +453,8 @@ the format block to specify different layouts for different formats: ```ruby class UserMailer < ApplicationMailer - def welcome_email(user) - mail(to: user.email) do |format| + def welcome_email + mail(to: params[:user].email) do |format| format.html { render layout: 'my_layout' } format.text end @@ -477,7 +477,7 @@ special URL that renders them. In the above example, the preview class for ```ruby class UserMailerPreview < ActionMailer::Preview def welcome_email - UserMailer.welcome_email(User.first) + UserMailer.welcome_email.with(user: User.first) end end ``` @@ -594,12 +594,12 @@ mailer action. ```ruby class UserMailer < ApplicationMailer - def welcome_email(user, company) - @user = user + def welcome_email + @user = params[:user] @url = user_url(@user) - delivery_options = { user_name: company.smtp_user, - password: company.smtp_password, - address: company.smtp_host } + delivery_options = { user_name: params[:company].smtp_user, + password: params[:company].smtp_password, + address: params[:company].smtp_host } mail(to: @user.email, subject: "Please see the Terms and Conditions attached", delivery_method_options: delivery_options) @@ -616,9 +616,9 @@ will default to `text/plain` otherwise. ```ruby class UserMailer < ApplicationMailer - def welcome_email(user, email_body) - mail(to: user.email, - body: email_body, + def welcome_email + mail(to: params[:user].email, + body: params[:email_body], content_type: "text/html", subject: "Already rendered!") end @@ -677,24 +677,43 @@ Action Mailer allows for you to specify a `before_action`, `after_action` and * You could use a `before_action` to populate the mail object with defaults, delivery_method_options or insert default headers and attachments. +```ruby +class InvitationsMailer < ApplicationMailer + before_action { @inviter, @invitee = params[:inviter], params[:invitee] } + before_action { @account = params[:inviter].account } + + default to: -> { @invitee.email_address }, + from: -> { common_address(@inviter) }, + reply_to: -> { @inviter.email_address_with_name } + + def account_invitation + mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})" + end + + def project_invitation + @project = params[:project] + @summarizer = ProjectInvitationSummarizer.new(@project.bucket) + + mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})" + end +end +``` + * You could use an `after_action` to do similar setup as a `before_action` but using instance variables set in your mailer action. ```ruby class UserMailer < ApplicationMailer + before_action { @business, @user = params[:business], params[:user] } + after_action :set_delivery_options, :prevent_delivery_to_guests, :set_business_headers - def feedback_message(business, user) - @business = business - @user = user - mail + def feedback_message end - def campaign_message(business, user) - @business = business - @user = user + def campaign_message end private -- cgit v1.2.3 From 5d8945fd49737a58595751f34dbca95ea5f9128c Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sun, 24 Sep 2017 11:25:15 +0900 Subject: Fix email in Active Support Instrumentation [ci skip] * `s/ddh/dhh/` --- guides/source/active_support_instrumentation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 03c9183eb3..ff4288a7f5 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -304,7 +304,7 @@ Action Mailer mailer: "Notification", message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail", subject: "Rails Guides", - to: ["users@rails.com", "ddh@rails.com"], + to: ["users@rails.com", "dhh@rails.com"], from: ["me@rails.com"], date: Sat, 10 Mar 2012 14:18:09 +0100, mail: "..." # omitted for brevity @@ -330,7 +330,7 @@ Action Mailer mailer: "Notification", message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail", subject: "Rails Guides", - to: ["users@rails.com", "ddh@rails.com"], + to: ["users@rails.com", "dhh@rails.com"], from: ["me@rails.com"], date: Sat, 10 Mar 2012 14:18:09 +0100, mail: "..." # omitted for brevity -- cgit v1.2.3 From 8b0af54bbe5ab8b598e980013dd53a50d819b636 Mon Sep 17 00:00:00 2001 From: Michael Coyne Date: Sat, 23 Sep 2017 17:18:01 -0400 Subject: Add key rotation cookies middleware Using the action_dispatch.cookies_rotations interface, key rotation is now possible with cookies. Thus the secret_key_base as well as salts, ciphers, and digests, can be rotated without expiring sessions. --- actionpack/CHANGELOG.md | 9 + .../lib/action_dispatch/middleware/cookies.rb | 182 ++++++------- actionpack/lib/action_dispatch/railtie.rb | 6 +- actionpack/test/controller/flash_test.rb | 4 +- .../controller/request_forgery_protection_test.rb | 4 +- actionpack/test/dispatch/cookies_test.rb | 289 ++++++++++++--------- actionpack/test/dispatch/routing_test.rb | 4 + .../test/dispatch/session/cookie_store_test.rb | 4 + guides/source/configuring.md | 11 + guides/source/security.md | 130 +++++++-- railties/lib/rails/application.rb | 1 + 11 files changed, 396 insertions(+), 248 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index a53d8efee1..1d4b27a0f9 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,12 @@ +* Simplify cookies middleware with key rotation support + + Use the `rotate` method for both `MessageEncryptor` and + `MessageVerifier` to add key rotation support for encrypted and + signed cookies. This also helps simplify support for legacy cookie + security. + + *Michael J Coyne* + * Use Capybara registered `:puma` server config. The Capybara registered `:puma` server ensures the puma server is run in process so diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 845df500d8..b3831649a8 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -49,6 +49,18 @@ module ActionDispatch get_header Cookies::AUTHENTICATED_ENCRYPTED_COOKIE_SALT end + def use_authenticated_cookie_encryption + get_header Cookies::USE_AUTHENTICATED_COOKIE_ENCRYPTION + end + + def encrypted_cookie_cipher + get_header Cookies::ENCRYPTED_COOKIE_CIPHER + end + + def signed_cookie_digest + get_header Cookies::SIGNED_COOKIE_DIGEST + end + def secret_token get_header Cookies::SECRET_TOKEN end @@ -64,6 +76,11 @@ module ActionDispatch def cookies_digest get_header Cookies::COOKIES_DIGEST end + + def cookies_rotations + get_header Cookies::COOKIES_ROTATIONS + end + # :startdoc: end @@ -157,10 +174,14 @@ module ActionDispatch ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt".freeze ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "action_dispatch.authenticated_encrypted_cookie_salt".freeze + USE_AUTHENTICATED_COOKIE_ENCRYPTION = "action_dispatch.use_authenticated_cookie_encryption".freeze + ENCRYPTED_COOKIE_CIPHER = "action_dispatch.encrypted_cookie_cipher".freeze + SIGNED_COOKIE_DIGEST = "action_dispatch.signed_cookie_digest".freeze SECRET_TOKEN = "action_dispatch.secret_token".freeze SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze COOKIES_DIGEST = "action_dispatch.cookies_digest".freeze + COOKIES_ROTATIONS = "action_dispatch.cookies_rotations".freeze # Cookies can typically store 4096 bytes. MAX_COOKIE_SIZE = 4096 @@ -201,12 +222,7 @@ module ActionDispatch # # cookies.signed[:discount] # => 45 def signed - @signed ||= - if upgrade_legacy_signed_cookies? - UpgradeLegacySignedCookieJar.new(self) - else - SignedCookieJar.new(self) - end + @signed ||= SignedKeyRotatingCookieJar.new(self) end # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read. @@ -223,18 +239,11 @@ module ActionDispatch # Example: # # cookies.encrypted[:discount] = 45 - # # => Set-Cookie: discount=ZS9ZZ1R4cG1pcUJ1bm80anhQang3dz09LS1mbDZDSU5scGdOT3ltQ2dTdlhSdWpRPT0%3D--ab54663c9f4e3bc340c790d6d2b71e92f5b60315; path=/ + # # => Set-Cookie: discount=DIQ7fw==--K3n//8vvnSbGq9dA--7Xh91HfLpwzbj1czhBiwOg==; path=/ # # cookies.encrypted[:discount] # => 45 def encrypted - @encrypted ||= - if upgrade_legacy_signed_cookies? - UpgradeLegacyEncryptedCookieJar.new(self) - elsif upgrade_legacy_hmac_aes_cbc_cookies? - UpgradeLegacyHmacAesCbcCookieJar.new(self) - else - EncryptedCookieJar.new(self) - end + @encrypted ||= EncryptedKeyRotatingCookieJar.new(self) end # Returns the +signed+ or +encrypted+ jar, preferring +encrypted+ if +secret_key_base+ is set. @@ -256,33 +265,17 @@ module ActionDispatch def upgrade_legacy_hmac_aes_cbc_cookies? request.secret_key_base.present? && - request.authenticated_encrypted_cookie_salt.present? && request.encrypted_signed_cookie_salt.present? && - request.encrypted_cookie_salt.present? + request.encrypted_cookie_salt.present? && + request.use_authenticated_cookie_encryption end - end - - # Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream - # to the Message{Encryptor,Verifier} allows us to handle the - # (de)serialization step within the cookie jar, which gives us the - # opportunity to detect and migrate legacy cookies. - module VerifyAndUpgradeLegacySignedMessage # :nodoc: - def initialize(*args) - super - @legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, serializer: ActiveSupport::MessageEncryptor::NullSerializer) - end - def verify_and_upgrade_legacy_signed_message(name, signed_message) - deserialize(name, @legacy_verifier.verify(signed_message)).tap do |value| - self[name] = { value: value } + def encrypted_cookie_cipher + request.encrypted_cookie_cipher || "aes-256-gcm" end - rescue ActiveSupport::MessageVerifier::InvalidSignature - nil - end - private - def parse(name, signed_message) - super || verify_and_upgrade_legacy_signed_message(name, signed_message) + def signed_cookie_digest + request.signed_cookie_digest || "SHA1" end end @@ -524,6 +517,7 @@ module ActionDispatch module SerializedCookieJars # :nodoc: MARSHAL_SIGNATURE = "\x04\x08".freeze + SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer protected def needs_migration?(value) @@ -534,12 +528,16 @@ module ActionDispatch serializer.dump(value) end - def deserialize(name, value) + def deserialize(name) + rotate = false + value = yield -> { rotate = true } + if value - if needs_migration?(value) - Marshal.load(value).tap do |v| - self[name] = { value: v } - end + case + when needs_migration?(value) + self[name] = Marshal.load(value) + when rotate + self[name] = serializer.load(value) else serializer.load(value) end @@ -561,24 +559,31 @@ module ActionDispatch def digest request.cookies_digest || "SHA1" end - - def key_generator - request.key_generator - end end - class SignedCookieJar < AbstractCookieJar # :nodoc: + class SignedKeyRotatingCookieJar < AbstractCookieJar # :nodoc: include SerializedCookieJars def initialize(parent_jar) super - secret = key_generator.generate_key(request.signed_cookie_salt) - @verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) + + secret = request.key_generator.generate_key(request.signed_cookie_salt) + @verifier = ActiveSupport::MessageVerifier.new(secret, digest: signed_cookie_digest, serializer: SERIALIZER) + + request.cookies_rotations.signed.each do |rotation_options| + @verifier.rotate serializer: SERIALIZER, **rotation_options + end + + if upgrade_legacy_signed_cookies? + @verifier.rotate raw_key: request.secret_token, serializer: SERIALIZER + end end private def parse(name, signed_message) - deserialize name, @verifier.verified(signed_message) + deserialize(name) do |rotate| + @verifier.verified(signed_message, on_rotation: rotate) + end end def commit(options) @@ -588,37 +593,38 @@ module ActionDispatch end end - # UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if - # secrets.secret_token and secret_key_base are both set. It reads - # legacy cookies signed with the old dummy key generator and signs and - # re-saves them using the new key generator to provide a smooth upgrade path. - class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc: - include VerifyAndUpgradeLegacySignedMessage - end - - class EncryptedCookieJar < AbstractCookieJar # :nodoc: + class EncryptedKeyRotatingCookieJar < AbstractCookieJar # :nodoc: include SerializedCookieJars def initialize(parent_jar) super - if ActiveSupport::LegacyKeyGenerator === key_generator - raise "You didn't set secret_key_base, which is required for this cookie jar. " \ - "Read the upgrade documentation to learn more about this new config option." + key_len = ActiveSupport::MessageEncryptor.key_len(encrypted_cookie_cipher) + secret = request.key_generator.generate_key(request.authenticated_encrypted_cookie_salt, key_len) + @encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: encrypted_cookie_cipher, serializer: SERIALIZER) + + request.cookies_rotations.encrypted.each do |rotation_options| + @encryptor.rotate serializer: SERIALIZER, **rotation_options end - cipher = "aes-256-gcm" - key_len = ActiveSupport::MessageEncryptor.key_len(cipher) - secret = key_generator.generate_key(request.authenticated_encrypted_cookie_salt || "")[0, key_len] + if upgrade_legacy_hmac_aes_cbc_cookies? + @encryptor.rotate \ + key_generator: request.key_generator, salt: request.encrypted_cookie_salt, signed_salt: request.encrypted_signed_cookie_salt, + cipher: "aes-256-cbc", digest: digest, serializer: SERIALIZER + end - @encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: ActiveSupport::MessageEncryptor::NullSerializer) + if upgrade_legacy_signed_cookies? + @legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, digest: digest, serializer: SERIALIZER) + end end private def parse(name, encrypted_message) - deserialize name, @encryptor.decrypt_and_verify(encrypted_message) - rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage - nil + deserialize(name) do |rotate| + @encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate) + end + rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature + parse_legacy_signed_message(name, encrypted_message) end def commit(options) @@ -626,39 +632,15 @@ module ActionDispatch raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE end - end - # UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore - # instead of EncryptedCookieJar if secrets.secret_token and secret_key_base - # are both set. It reads legacy cookies signed with the old dummy key generator and - # encrypts and re-saves them using the new key generator to provide a smooth upgrade path. - class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc: - include VerifyAndUpgradeLegacySignedMessage - end + def parse_legacy_signed_message(name, legacy_signed_message) + if defined?(@legacy_verifier) + deserialize(name) do |rotate| + rotate.call - # UpgradeLegacyHmacAesCbcCookieJar is used by ActionDispatch::Session::CookieStore - # to upgrade cookies encrypted with AES-256-CBC with HMAC to AES-256-GCM - class UpgradeLegacyHmacAesCbcCookieJar < EncryptedCookieJar - def initialize(parent_jar) - super - - secret = key_generator.generate_key(request.encrypted_cookie_salt || "")[0, ActiveSupport::MessageEncryptor.key_len] - sign_secret = key_generator.generate_key(request.encrypted_signed_cookie_salt || "") - - @legacy_encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) - end - - def decrypt_and_verify_legacy_encrypted_message(name, signed_message) - deserialize(name, @legacy_encryptor.decrypt_and_verify(signed_message)).tap do |value| - self[name] = { value: value } - end - rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage - nil - end - - private - def parse(name, signed_message) - super || decrypt_and_verify_legacy_encrypted_message(name, signed_message) + @legacy_verifier.verified(legacy_signed_message) + end + end end end diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 4743a7ce61..855f2ffa47 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "action_dispatch" +require "active_support/messages/rotation_configuration" module ActionDispatch class Railtie < Rails::Railtie # :nodoc: @@ -18,6 +19,7 @@ module ActionDispatch config.action_dispatch.signed_cookie_salt = "signed cookie" config.action_dispatch.encrypted_cookie_salt = "encrypted cookie" config.action_dispatch.encrypted_signed_cookie_salt = "signed encrypted cookie" + config.action_dispatch.authenticated_encrypted_cookie_salt = "authenticated encrypted cookie" config.action_dispatch.use_authenticated_cookie_encryption = false config.action_dispatch.perform_deep_munge = true @@ -27,6 +29,8 @@ module ActionDispatch "X-Content-Type-Options" => "nosniff" } + config.action_dispatch.cookies_rotations = ActiveSupport::Messages::RotationConfiguration.new + config.eager_load_namespaces << ActionDispatch initializer "action_dispatch.configure" do |app| @@ -39,8 +43,6 @@ module ActionDispatch ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses) ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates) - config.action_dispatch.authenticated_encrypted_cookie_salt = "authenticated encrypted cookie" if config.action_dispatch.use_authenticated_cookie_encryption - config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil? ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index d92ae0b817..34bc2c0caa 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "abstract_unit" -require "active_support/key_generator" +require "active_support/messages/rotation_configuration" class FlashTest < ActionController::TestCase class TestController < ActionController::Base @@ -243,6 +243,7 @@ end class FlashIntegrationTest < ActionDispatch::IntegrationTest SessionKey = "_myapp_session" Generator = ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") + Rotations = ActiveSupport::Messages::RotationConfiguration.new class TestController < ActionController::Base add_flash_types :bar @@ -348,6 +349,7 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest args[0] ||= {} args[0][:env] ||= {} args[0][:env]["action_dispatch.key_generator"] ||= Generator + args[0][:env]["action_dispatch.cookies_rotations"] = Rotations super(path, *args) end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 12ae95d602..eb3d2f34a8 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -2,6 +2,7 @@ require "abstract_unit" require "active_support/log_subscriber/test_helper" +require "active_support/messages/rotation_configuration" # common controller actions module RequestForgeryProtectionActions @@ -630,13 +631,14 @@ end class RequestForgeryProtectionControllerUsingNullSessionTest < ActionController::TestCase class NullSessionDummyKeyGenerator - def generate_key(secret) + def generate_key(secret, length = nil) "03312270731a2ed0d11ed091c2338a06" end end def setup @request.env[ActionDispatch::Cookies::GENERATOR_KEY] = NullSessionDummyKeyGenerator.new + @request.env[ActionDispatch::Cookies::COOKIES_ROTATIONS] = ActiveSupport::Messages::RotationConfiguration.new end test "should allow to set signed cookies" do diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index cb225c0f62..706d0be9c2 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -3,7 +3,7 @@ require "abstract_unit" require "openssl" require "active_support/key_generator" -require "active_support/message_verifier" +require "active_support/messages/rotation_configuration" class CookieJarTest < ActiveSupport::TestCase attr_reader :request @@ -287,15 +287,25 @@ class CookiesTest < ActionController::TestCase tests TestController - SALT = "b3c631c314c0bbca50c1b2843150fe33" + SECRET_KEY_BASE = "b3c631c314c0bbca50c1b2843150fe33" + SIGNED_COOKIE_SALT = "signed cookie" + ENCRYPTED_COOKIE_SALT = "encrypted cookie" + ENCRYPTED_SIGNED_COOKIE_SALT = "sigend encrypted cookie" + AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "authenticated encrypted cookie" def setup super - @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new(SALT, iterations: 2) + @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new(SECRET_KEY_BASE, iterations: 2) + @request.env["action_dispatch.cookies_rotations"] = ActiveSupport::Messages::RotationConfiguration.new - @request.env["action_dispatch.signed_cookie_salt"] = - @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] = SALT + @request.env["action_dispatch.secret_key_base"] = SECRET_KEY_BASE + @request.env["action_dispatch.use_authenticated_cookie_encryption"] = true + + @request.env["action_dispatch.signed_cookie_salt"] = SIGNED_COOKIE_SALT + @request.env["action_dispatch.encrypted_cookie_salt"] = ENCRYPTED_COOKIE_SALT + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = ENCRYPTED_SIGNED_COOKIE_SALT + @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] = AUTHENTICATED_ENCRYPTED_COOKIE_SALT @request.host = "www.nextangle.com" end @@ -430,28 +440,96 @@ class CookiesTest < ActionController::TestCase assert_equal 45, cookies.signed[:user_id] key_generator = @request.env["action_dispatch.key_generator"] - signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] - secret = key_generator.generate_key(signed_cookie_salt) + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: "SHA1") assert_equal verifier.generate(45), cookies[:user_id] end def test_signed_cookie_using_custom_digest - @request.env["action_dispatch.cookies_digest"] = "SHA256" + @request.env["action_dispatch.signed_cookie_digest"] = "SHA256" + get :set_signed_cookie cookies = @controller.send :cookies assert_not_equal 45, cookies[:user_id] assert_equal 45, cookies.signed[:user_id] key_generator = @request.env["action_dispatch.key_generator"] - signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] - secret = key_generator.generate_key(signed_cookie_salt) + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: "SHA256") assert_equal verifier.generate(45), cookies[:user_id] end + def test_signed_cookie_rotations_with_secret_key_base_and_digest + rotated_secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" + rotated_salt = "signed cookie" + + @request.env["action_dispatch.signed_cookie_digest"] = "SHA256" + @request.env["action_dispatch.cookies_rotations"].rotate :signed, + secret: rotated_secret_key_base, salt: rotated_salt, digest: "SHA1" + + old_secret = ActiveSupport::KeyGenerator.new(rotated_secret_key_base, iterations: 1000).generate_key(rotated_salt) + old_message = ActiveSupport::MessageVerifier.new(old_secret, digest: "SHA1", serializer: Marshal).generate(45) + + @request.headers["Cookie"] = "user_id=#{old_message}" + + get :get_signed_cookie + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA256", serializer: Marshal) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_signed_cookie_rotations_with_raw_key_and_digest + rotated_raw_key = "b3c631c314c0bbca50c1b2843150fe33" + + @request.env["action_dispatch.signed_cookie_digest"] = "SHA256" + @request.env["action_dispatch.cookies_rotations"].rotate :signed, + raw_key: rotated_raw_key, digest: "SHA1" + + old_message = ActiveSupport::MessageVerifier.new(rotated_raw_key, digest: "SHA1", serializer: Marshal).generate(45) + + @request.headers["Cookie"] = "user_id=#{old_message}" + + get :get_signed_cookie + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA256", serializer: Marshal) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_signed_cookie_with_legacy_secret_scheme + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + + old_message = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", digest: "SHA1", serializer: Marshal).generate(45) + + @request.headers["Cookie"] = "user_id=#{old_message}" + get :get_signed_cookie + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key("signed cookie") + verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA1", serializer: Marshal) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_tampered_with_signed_cookie + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: "SHA1") + message = verifier.generate(45) + + @request.headers["Cookie"] = "user_id=#{Marshal.dump 45}--#{message.split("--").last}" + get :get_signed_cookie + assert_nil @controller.send(:cookies).signed[:user_id] + end + def test_signed_cookie_using_default_serializer get :set_signed_cookie cookies = @controller.send :cookies @@ -494,8 +572,7 @@ class CookiesTest < ActionController::TestCase @request.env["action_dispatch.cookies_serializer"] = :hybrid key_generator = @request.env["action_dispatch.key_generator"] - signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] - secret = key_generator.generate_key(signed_cookie_salt) + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) marshal_value = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal).generate(45) @request.headers["Cookie"] = "user_id=#{marshal_value}" @@ -514,8 +591,8 @@ class CookiesTest < ActionController::TestCase @request.env["action_dispatch.cookies_serializer"] = :hybrid key_generator = @request.env["action_dispatch.key_generator"] - signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] - secret = key_generator.generate_key(signed_cookie_salt) + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + json_value = ActiveSupport::MessageVerifier.new(secret, serializer: JSON).generate(45) @request.headers["Cookie"] = "user_id=#{json_value}" @@ -578,11 +655,10 @@ class CookiesTest < ActionController::TestCase def test_encrypted_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json @request.env["action_dispatch.cookies_serializer"] = :hybrid - cipher = "aes-256-gcm" - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: Marshal) + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], 32) + encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal) marshal_value = encryptor.encrypt_and_sign("bar") @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape marshal_value}" @@ -592,7 +668,7 @@ class CookiesTest < ActionController::TestCase assert_not_equal "bar", cookies[:foo] assert_equal "bar", cookies.encrypted[:foo] - json_encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON) + json_encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: JSON) assert_not_nil @response.cookies["foo"] assert_equal "bar", json_encryptor.decrypt_and_verify(@response.cookies["foo"]) end @@ -600,11 +676,10 @@ class CookiesTest < ActionController::TestCase def test_encrypted_cookie_using_hybrid_serializer_can_read_from_json_dumped_value @request.env["action_dispatch.cookies_serializer"] = :hybrid - cipher = "aes-256-gcm" - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON) + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], 32) + encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: JSON) json_value = encryptor.encrypt_and_sign("bar") @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape json_value}" @@ -691,65 +766,8 @@ class CookiesTest < ActionController::TestCase } end - def test_signed_uses_signed_cookie_jar_if_only_secret_token_is_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = nil - get :set_signed_cookie - assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed - end - - def test_signed_uses_signed_cookie_jar_if_only_secret_key_base_is_set - @request.env["action_dispatch.secret_token"] = nil - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - get :set_signed_cookie - assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed - end - - def test_signed_uses_upgrade_legacy_signed_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - get :set_signed_cookie - assert_kind_of ActionDispatch::Cookies::UpgradeLegacySignedCookieJar, cookies.signed - end - - def test_signed_or_encrypted_uses_signed_cookie_jar_if_only_secret_token_is_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = nil - get :get_encrypted_cookie - assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed_or_encrypted - end - - def test_signed_or_encrypted_uses_encrypted_cookie_jar_if_only_secret_key_base_is_set - @request.env["action_dispatch.secret_token"] = nil - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - get :get_encrypted_cookie - assert_kind_of ActionDispatch::Cookies::EncryptedCookieJar, cookies.signed_or_encrypted - end - - def test_signed_or_encrypted_uses_upgrade_legacy_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - get :get_encrypted_cookie - assert_kind_of ActionDispatch::Cookies::UpgradeLegacyEncryptedCookieJar, cookies.signed_or_encrypted - end - - def test_encrypted_uses_encrypted_cookie_jar_if_only_secret_key_base_is_set - @request.env["action_dispatch.secret_token"] = nil - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - get :get_encrypted_cookie - assert_kind_of ActionDispatch::Cookies::EncryptedCookieJar, cookies.encrypted - end - - def test_encrypted_uses_upgrade_legacy_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - get :get_encrypted_cookie - assert_kind_of ActionDispatch::Cookies::UpgradeLegacyEncryptedCookieJar, cookies.encrypted - end - def test_legacy_signed_cookie_is_read_and_transparently_upgraded_by_signed_cookie_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45) @@ -766,9 +784,6 @@ class CookiesTest < ActionController::TestCase def test_legacy_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee" - @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9" legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate("bar") @@ -777,17 +792,14 @@ class CookiesTest < ActionController::TestCase assert_equal "bar", @controller.send(:cookies).encrypted[:foo] - cipher = "aes-256-gcm" - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: Marshal) + secret = @request.env["action_dispatch.key_generator"].generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], 32) + encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal) assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) end def test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.cookies_serializer"] = :json @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45) @@ -805,7 +817,6 @@ class CookiesTest < ActionController::TestCase def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.cookies_serializer"] = :json @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate("bar") @@ -824,7 +835,6 @@ class CookiesTest < ActionController::TestCase def test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.cookies_serializer"] = :hybrid @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45) @@ -842,7 +852,6 @@ class CookiesTest < ActionController::TestCase def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.cookies_serializer"] = :hybrid @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate("bar") @@ -851,17 +860,15 @@ class CookiesTest < ActionController::TestCase assert_equal "bar", @controller.send(:cookies).encrypted[:foo] - cipher = "aes-256-gcm" salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON) + secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")] + encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: JSON) assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) end def test_legacy_marshal_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.cookies_serializer"] = :hybrid @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45) @@ -878,6 +885,8 @@ class CookiesTest < ActionController::TestCase def test_legacy_marshal_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.cookies_serializer"] = :hybrid + + @request.env["action_dispatch.use_authenticated_cookie_encryption"] = true @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" @@ -888,16 +897,14 @@ class CookiesTest < ActionController::TestCase assert_equal "bar", @controller.send(:cookies).encrypted[:foo] - cipher = "aes-256-gcm" salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON) + secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")] + encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: JSON) assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) end def test_legacy_signed_cookie_is_treated_as_nil_by_signed_cookie_jar_if_tampered @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" @request.headers["Cookie"] = "user_id=45" get :get_signed_cookie @@ -908,7 +915,6 @@ class CookiesTest < ActionController::TestCase def test_legacy_signed_cookie_is_treated_as_nil_by_encrypted_cookie_jar_if_tampered @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" @request.headers["Cookie"] = "foo=baz" get :get_encrypted_cookie @@ -918,17 +924,12 @@ class CookiesTest < ActionController::TestCase end def test_legacy_hmac_aes_cbc_encrypted_marshal_cookie_is_upgraded_to_authenticated_encrypted_cookie - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - - @request.env["action_dispatch.encrypted_cookie_salt"] = - @request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT - key_generator = @request.env["action_dispatch.key_generator"] encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"] - secret = key_generator.generate_key(encrypted_cookie_salt) + secret = key_generator.generate_key(encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len("aes-256-cbc")) sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt) - marshal_value = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: Marshal).encrypt_and_sign("bar") + marshal_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", serializer: Marshal).encrypt_and_sign("bar") @request.headers["Cookie"] = "foo=#{marshal_value}" @@ -938,27 +939,22 @@ class CookiesTest < ActionController::TestCase assert_not_equal "bar", cookies[:foo] assert_equal "bar", cookies.encrypted[:foo] - aead_cipher = "aes-256-gcm" aead_salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - aead_secret = key_generator.generate_key(aead_salt)[0, ActiveSupport::MessageEncryptor.key_len(aead_cipher)] - aead_encryptor = ActiveSupport::MessageEncryptor.new(aead_secret, cipher: aead_cipher, serializer: Marshal) + aead_secret = key_generator.generate_key(aead_salt, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")) + aead_encryptor = ActiveSupport::MessageEncryptor.new(aead_secret, cipher: "aes-256-gcm", serializer: Marshal) assert_equal "bar", aead_encryptor.decrypt_and_verify(@response.cookies["foo"]) end def test_legacy_hmac_aes_cbc_encrypted_json_cookie_is_upgraded_to_authenticated_encrypted_cookie @request.env["action_dispatch.cookies_serializer"] = :json - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - - @request.env["action_dispatch.encrypted_cookie_salt"] = - @request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT key_generator = @request.env["action_dispatch.key_generator"] encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"] - secret = key_generator.generate_key(encrypted_cookie_salt) + secret = key_generator.generate_key(encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len("aes-256-cbc")) sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt) - marshal_value = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret, serializer: JSON).encrypt_and_sign("bar") + marshal_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", serializer: JSON).encrypt_and_sign("bar") @request.headers["Cookie"] = "foo=#{marshal_value}" @@ -968,19 +964,17 @@ class CookiesTest < ActionController::TestCase assert_not_equal "bar", cookies[:foo] assert_equal "bar", cookies.encrypted[:foo] - aead_cipher = "aes-256-gcm" aead_salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - aead_secret = key_generator.generate_key(aead_salt)[0, ActiveSupport::MessageEncryptor.key_len(aead_cipher)] - aead_encryptor = ActiveSupport::MessageEncryptor.new(aead_secret, cipher: aead_cipher, serializer: JSON) + aead_secret = key_generator.generate_key(aead_salt)[0, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")] + aead_encryptor = ActiveSupport::MessageEncryptor.new(aead_secret, cipher: "aes-256-gcm", serializer: JSON) assert_equal "bar", aead_encryptor.decrypt_and_verify(@response.cookies["foo"]) end def test_legacy_hmac_aes_cbc_encrypted_cookie_using_64_byte_key_is_upgraded_to_authenticated_encrypted_cookie @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - - @request.env["action_dispatch.encrypted_cookie_salt"] = - @request.env["action_dispatch.encrypted_signed_cookie_salt"] = SALT + @request.env["action_dispatch.encrypted_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" # Cookie generated with 64 bytes secret message = ["566d4e75536d686e633246564e6b493062557079626c566d51574d30515430394c53315665564a694e4563786555744f57537454576b396a5a31566a626e52525054303d2d2d34663234333330623130623261306163363562316266323335396164666364613564643134623131"].pack("H*") @@ -991,15 +985,60 @@ class CookiesTest < ActionController::TestCase cookies = @controller.send :cookies assert_not_equal "bar", cookies[:foo] assert_equal "bar", cookies.encrypted[:foo] - cipher = "aes-256-gcm" salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: Marshal) + secret = @request.env["action_dispatch.key_generator"].generate_key(salt, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")) + encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal) assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) end + def test_encrypted_cookie_rotations_with_secret_and_salt + rotated_secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" + rotated_salt = "authenticated encrypted cookie" + + @request.env["action_dispatch.encrypted_cookie_cipher"] = "aes-256-gcm" + @request.env["action_dispatch.cookies_rotations"].rotate :encrypted, + secret: rotated_secret_key_base, salt: rotated_salt, cipher: "aes-256-gcm" + + key_len = ActiveSupport::MessageEncryptor.key_len("aes-256-gcm") + + old_secret = ActiveSupport::KeyGenerator.new(rotated_secret_key_base, iterations: 1000).generate_key(rotated_salt, key_len) + old_message = ActiveSupport::MessageEncryptor.new(old_secret, cipher: "aes-256-gcm", serializer: Marshal).encrypt_and_sign("bar") + + @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape old_message}" + + get :get_encrypted_cookie + assert_equal "bar", @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], key_len) + encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal) + assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + + def test_encrypted_cookie_rotations_with_raw_key + raw_key = "b3c631c314c0bbca50c1b2843150fe33" + + @request.env["action_dispatch.encrypted_cookie_cipher"] = "aes-256-gcm" + @request.env["action_dispatch.cookies_rotations"].rotate :encrypted, + raw_key: raw_key, cipher: "aes-256-gcm" + + key_len = ActiveSupport::MessageEncryptor.key_len("aes-256-gcm") + + old_message = ActiveSupport::MessageEncryptor.new(raw_key, cipher: "aes-256-gcm", serializer: Marshal).encrypt_and_sign(45) + + @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape old_message}" + + get :get_encrypted_cookie + assert_equal 45, @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], key_len) + encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal) + assert_equal 45, encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + def test_cookie_with_all_domain_option get :set_cookie_with_domain assert_response :success diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 446b65a9b9..44f902c163 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -3,6 +3,7 @@ require "erb" require "abstract_unit" require "controller/fake_controllers" +require "active_support/messages/rotation_configuration" class TestRoutingMapper < ActionDispatch::IntegrationTest SprocketsApp = lambda { |env| @@ -4947,6 +4948,7 @@ end class FlashRedirectTest < ActionDispatch::IntegrationTest SessionKey = "_myapp_session" Generator = ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") + Rotations = ActiveSupport::Messages::RotationConfiguration.new class KeyGeneratorMiddleware def initialize(app) @@ -4955,6 +4957,8 @@ class FlashRedirectTest < ActionDispatch::IntegrationTest def call(env) env["action_dispatch.key_generator"] ||= Generator + env["action_dispatch.cookies_rotations"] ||= Rotations + @app.call(env) end end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 6517cf4c99..cf51c47068 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -3,11 +3,13 @@ require "abstract_unit" require "stringio" require "active_support/key_generator" +require "active_support/messages/rotation_configuration" class CookieStoreTest < ActionDispatch::IntegrationTest SessionKey = "_myapp_session" SessionSecret = "b3c631c314c0bbca50c1b2843150fe33" Generator = ActiveSupport::LegacyKeyGenerator.new(SessionSecret) + Rotations = ActiveSupport::Messages::RotationConfiguration.new Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, digest: "SHA1") SignedBar = Verifier.generate(foo: "bar", session_id: SecureRandom.hex(16)) @@ -346,6 +348,8 @@ class CookieStoreTest < ActionDispatch::IntegrationTest args[0] ||= {} args[0][:headers] ||= {} args[0][:headers]["action_dispatch.key_generator"] ||= Generator + args[0][:headers]["action_dispatch.cookies_rotations"] ||= Rotations + super(path, *args) end diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 1c720ad82f..86c8364d83 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -487,6 +487,17 @@ Defaults to `'signed cookie'`. authenticated encrypted cookie salt. Defaults to `'authenticated encrypted cookie'`. +* `config.action_dispatch.encrypted_cookie_cipher` sets the cipher to be + used for encrypted cookies. This defaults to `"aes-256-gcm"`. + +* `config.action_dispatch.signed_cookie_digest` sets the digest to be + used for signed cookies. This defaults to `"SHA1"`. + +* `config.action_dispatch.cookies_rotations` is set to an instance of + [RotationConfiguration](http://api.rubyonrails.org/classes/ActiveSupport/RotationConfiguration.html). + It provides an interface for rotating keys, salts, ciphers, and + digests for encrypted and signed cookies. + * `config.action_dispatch.perform_deep_munge` configures whether `deep_munge` method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation) for more information. It defaults to `true`. diff --git a/guides/source/security.md b/guides/source/security.md index 0b2d8de0fb..b0b71cad7d 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -85,37 +85,129 @@ This will also be a good idea, if you modify the structure of an object and old * _Critical data should not be stored in session_. If the user clears their cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data. -### Session Storage +### Encrypted Session Storage NOTE: _Rails provides several storage mechanisms for the session hashes. The most important is `ActionDispatch::Session::CookieStore`._ -Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session ID. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it: +The `CookieStore` saves the session hash directly in a cookie on the +client-side. The server retrieves the session hash from the cookie and +eliminates the need for a session ID. That will greatly increase the +speed of the application, but it is a controversial storage option and +you have to think about the security implications and storage +limitations of it: + +* Cookies imply a strict size limit of 4kB. This is fine as you should + not store large amounts of data in a session anyway, as described + before. Storing the current user's database id in a session is common + practice. + +* Session cookies do not invalidate themselves and can be maliciously + reused. It may be a good idea to have your application invalidate old + session cookies using a stored timestamp. + +The `CookieStore` uses the +[encrypted](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-encrypted) +cookie jar to provide a secure, encrypted location to store session +data. Cookie-based sessions thus provide both integrity as well as +confidentiality to their contents. The encryption key, as well as the +verification key used for +[signed](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-signed) +cookies, is derived from the `secret_key_base` configuration value. + +As of Rails 5.2 encrypted cookies and sessions are protected using AES +GCM encryption. This form of encryption is a type of Authenticated +Encryption and couples authentication and encryption in single step +while also producing shorter ciphertexts as compared to other +algorithms previously used. The key for cookies encrypted with AES GCM +are derived using a salt value defined by the +`config.action_dispatch.authenticated_encrypted_cookie_salt` +configuration value. + +Prior to this version, encrypted cookies were secured using AES in CBC +mode with HMAC using SHA1 for authentication. The keys for this type of +encryption and for HMAC verification were derived via the salts defined +by `config.action_dispatch.encrypted_cookie_salt` and +`config.action_dispatch.encrypted_signed_cookie_salt` respectively. + +Prior to Rails version 4 in both versions 2 and 3, session cookies were +protected using only HMAC verification. As such, these session cookies +only provided integrity to their content because the actual session data +was stored in plaintext encoded as base64. This is how `signed` cookies +work in the current version of Rails. These kinds of cookies are still +useful for protecting the integrity of certain client-stored data and +information. + +__Do not use a trivial secret for the `secret_key_base`, i.e. a word +from a dictionary, or one which is shorter than 30 characters! Instead +use `rails secret` to generate secret keys!__ + +It is also important to use different salt values for encrypted and +signed cookies. Using the same value for different salt configuration +values may lead to the same derived key being used for different +security features which in turn may weaken the strength of the key. -* Cookies imply a strict size limit of 4kB. This is fine as you should not store large amounts of data in a session anyway, as described before. _Storing the current user's database id in a session is usually ok_. +In test and development applications get a `secret_key_base` derived from the app name. Other environments must use a random key present in `config/credentials.yml.enc`, shown here in its decrypted state: -* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret (`secrets.secret_token`) and inserted into the end of the cookie. + secret_key_base: 492f... -In Rails 4, encrypted cookies through AES in CBC mode with HMAC using SHA1 for -verification was introduced. This prevents the user from accessing and tampering -the content of the cookie. Thus the session becomes a more secure place to store -data. The encryption is performed using a server-side `secret_key_base`. -Two salts are used when deriving keys for encryption and verification. These -salts are set via the `config.action_dispatch.encrypted_cookie_salt` and -`config.action_dispatch.encrypted_signed_cookie_salt` configuration values. +If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. -Rails 5.2 uses AES-GCM for the encryption which couples authentication -and encryption in one faster step and produces shorter ciphertexts. +### Rotating Keys for Encrypted and Signed Cookies -Encrypted cookies are automatically upgraded if the -`config.action_dispatch.use_authenticated_cookie_encryption` is enabled. +It is possible to rotate the `secret_key_base` as well as the salts, +ciphers, and digests used for both encrypted and signed cookies. Rotating +the `secret_key_base` is necessary if the value was exposed or leaked. +It is also useful to rotate this value for other more benign reasons, +such as an employee leaving your organization or changing hosting +environments. -_Do not use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters! Instead use `rails secret` to generate secret keys!_ +Key rotations can be defined through the +`config.action_dispatch.cookies_rotations` configuration value. This +value is set to an instance of +[RotationConfiguration](http://api.rubyonrails.org/classes/ActiveSupport/RotationConfiguration.html) +which provides an interface for rotating signed and encrypted cookie +keys, salts, digests, and ciphers. -In test and development applications get a `secret_key_base` derived from the app name. Other environments must use a random key present in `config/credentials.yml.enc`, shown here in its decrypted state: +For example, suppose we want to rotate out an old `secret_key_base`, we +can define a signed and encrypted key rotation as follows: - secret_key_base: 492f... +```ruby +config.action_dispatch.cookies_rotations.rotate :encrypted, + cipher: "aes-256-gcm", + secret: Rails.application.credentials.old_secret_key_base, + salt: config.action_dispatch.authenticated_encrypted_cookie_salt -If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. +config.action_dispatch.cookies_rotations.rotate :signed, + digest: "SHA1", + secret: Rails.application.credentials.old_secret_key_base, + salt: config.action_dispatch.signed_cookie_salt +``` + +Multiple rotations are possible by calling `rotate` multiple times. For +example, suppose we want to use SHA512 for signed cookies while rotating +out SHA256 and SHA1 digests using the same `secret_key_base` + +```ruby +config.action_dispatch.signed_cookie_digest = "SHA512" + +config.action_dispatch.cookies_rotations.rotate :signed, + digest: "SHA256", + secret: Rails.application.credentials.secret_key_base, + salt: config.action_dispatch.signed_cookie_salt + +config.action_dispatch.cookies_rotations.rotate :signed, + digest: "SHA1", + secret: Rails.application.credentials.secret_key_base, + salt: config.action_dispatch.signed_cookie_salt +``` + +For more details on key rotation with encrypted and signed messages as +well as the various options the `rotate` method accepts, please refer to +the +[MessageEncryptor API](api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html) +and +[MessageVerifier API](api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html) +documentation. ### Replay Attacks for CookieStore Sessions diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index f691156921..24f5eeae87 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -259,6 +259,7 @@ module Rails "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt, "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt, "action_dispatch.authenticated_encrypted_cookie_salt" => config.action_dispatch.authenticated_encrypted_cookie_salt, + "action_dispatch.use_authenticated_cookie_encryption" => config.action_dispatch.use_authenticated_cookie_encryption, "action_dispatch.encrypted_cookie_cipher" => config.action_dispatch.encrypted_cookie_cipher, "action_dispatch.signed_cookie_digest" => config.action_dispatch.signed_cookie_digest, "action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer, -- cgit v1.2.3 From abd4fd43692cd883068ad27f620fd4c00e546f91 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 24 Sep 2017 18:46:28 +0200 Subject: [ci skip] Fix the with order and explain it. --- guides/source/action_mailer_basics.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index a025fb773a..cb07781d1c 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -176,7 +176,7 @@ $ bin/rails db:migrate Now that we have a user model to play with, we will just edit the `app/controllers/users_controller.rb` make it instruct the `UserMailer` to deliver an email to the newly created user by editing the create action and inserting a -call to `UserMailer.welcome_email` right after the user is successfully saved. +call to `UserMailer.with(user: @user).welcome_email` right after the user is successfully saved. Action Mailer is nicely integrated with Active Job so you can send emails outside of the request-response cycle, so the user doesn't have to wait on it: @@ -191,7 +191,7 @@ class UsersController < ApplicationController respond_to do |format| if @user.save # Tell the UserMailer to send a welcome email after save - UserMailer.welcome_email.with(user: @user).deliver_later + UserMailer.with(user: @user).welcome_email.deliver_later format.html { redirect_to(@user, notice: 'User was successfully created.') } format.json { render json: @user, status: :created, location: @user } @@ -226,6 +226,11 @@ class SendWeeklySummary end ``` +Any key value pair passed to `with` just becomes the `params` for the mailer +action. So `with(user: @user, account: @user.account)` makes `params[:user]` and +`params[:account]` available in the mailer action. Just like controllers have +params. + The method `welcome_email` returns an `ActionMailer::MessageDelivery` object which can then just be told `deliver_now` or `deliver_later` to send itself out. The `ActionMailer::MessageDelivery` object is just a wrapper around a `Mail::Message`. If @@ -477,7 +482,7 @@ special URL that renders them. In the above example, the preview class for ```ruby class UserMailerPreview < ActionMailer::Preview def welcome_email - UserMailer.welcome_email.with(user: User.first) + UserMailer.with(user: User.first).welcome_email end end ``` -- cgit v1.2.3 From 92afe55b179152a5747b70cc5d5375395581b70f Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 24 Sep 2017 19:02:12 +0200 Subject: [ci skip] RotationConfiguration is an implementation detail, not public API. --- .../lib/active_support/messages/rotation_configuration.rb | 2 +- guides/source/security.md | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/activesupport/lib/active_support/messages/rotation_configuration.rb b/activesupport/lib/active_support/messages/rotation_configuration.rb index 12566bdb63..908658ff02 100644 --- a/activesupport/lib/active_support/messages/rotation_configuration.rb +++ b/activesupport/lib/active_support/messages/rotation_configuration.rb @@ -2,7 +2,7 @@ module ActiveSupport module Messages - class RotationConfiguration + class RotationConfiguration # :nodoc: attr_reader :signed, :encrypted def initialize diff --git a/guides/source/security.md b/guides/source/security.md index b0b71cad7d..994978b88b 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -161,12 +161,9 @@ It is also useful to rotate this value for other more benign reasons, such as an employee leaving your organization or changing hosting environments. -Key rotations can be defined through the -`config.action_dispatch.cookies_rotations` configuration value. This -value is set to an instance of -[RotationConfiguration](http://api.rubyonrails.org/classes/ActiveSupport/RotationConfiguration.html) -which provides an interface for rotating signed and encrypted cookie -keys, salts, digests, and ciphers. +Key rotations can be defined through +`config.action_dispatch.cookies_rotations` which provides an interface for +rotating signed and encrypted cookie keys, salts, digests, and ciphers. For example, suppose we want to rotate out an old `secret_key_base`, we can define a signed and encrypted key rotation as follows: @@ -185,7 +182,7 @@ config.action_dispatch.cookies_rotations.rotate :signed, Multiple rotations are possible by calling `rotate` multiple times. For example, suppose we want to use SHA512 for signed cookies while rotating -out SHA256 and SHA1 digests using the same `secret_key_base` +out SHA256 and SHA1 digests using the same `secret_key_base`: ```ruby config.action_dispatch.signed_cookie_digest = "SHA512" -- cgit v1.2.3 From 11444d7b58948648c3636b920cc839c61aa60c7e Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Mon, 25 Sep 2017 02:22:59 +0900 Subject: Refactor Css::Generators::ScaffoldGenerator * define source_root by source_root method * it don't create file, but copy it. --- railties/lib/rails/generators/css/scaffold/scaffold_generator.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb index 5996cb1483..d8eb4f2c7b 100644 --- a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb @@ -5,13 +5,13 @@ require_relative "../../named_base" module Css # :nodoc: module Generators # :nodoc: class ScaffoldGenerator < Rails::Generators::NamedBase # :nodoc: + source_root Rails::Generators::ScaffoldGenerator.source_root + # In order to allow the Sass generators to pick up the default Rails CSS and # transform it, we leave it in a standard location for the CSS stylesheet # generators to handle. For the simple, default case, just copy it over. def copy_stylesheet - dir = Rails::Generators::ScaffoldGenerator.source_root - file = File.join(dir, "scaffold.css") - create_file "app/assets/stylesheets/scaffold.css", File.read(file) + copy_file "scaffold.css", "app/assets/stylesheets/scaffold.css" end end end -- cgit v1.2.3 From 20ba2e762ceab098371122b0c02b4a90239d2ace Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 24 Sep 2017 19:46:10 +0200 Subject: Infer options from the primary verifier. Spares users from passing in non-changing values explicitly. [ Michael Coyne & Kasper Timm Hansen ] --- activesupport/lib/active_support/messages/rotator.rb | 11 +++++++---- activesupport/test/message_encryptor_test.rb | 20 ++++++++++++++++---- activesupport/test/message_verifier_test.rb | 8 ++++---- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/activesupport/lib/active_support/messages/rotator.rb b/activesupport/lib/active_support/messages/rotator.rb index 21ae643138..e18549d735 100644 --- a/activesupport/lib/active_support/messages/rotator.rb +++ b/activesupport/lib/active_support/messages/rotator.rb @@ -3,9 +3,10 @@ module ActiveSupport module Messages module Rotator # :nodoc: - def initialize(*args) + def initialize(*, **options) super + @options = options @rotations = [] end @@ -24,10 +25,12 @@ module ActiveSupport private def create_rotation(raw_key: nil, raw_signed_key: nil, **options) + options[:cipher] ||= @cipher + self.class.new \ raw_key || extract_key(options), raw_signed_key || extract_signing_key(options), - options.slice(:cipher, :digest, :serializer) + @options.merge(options.slice(:cipher, :digest, :serializer)) end def extract_key(cipher:, salt:, key_generator: nil, secret: nil, **) @@ -53,8 +56,8 @@ module ActiveSupport end private - def create_rotation(raw_key: nil, digest: nil, serializer: nil, **options) - self.class.new(raw_key || extract_key(options), digest: digest, serializer: serializer) + def create_rotation(raw_key: nil, **options) + self.class.new(raw_key || extract_key(options), @options.merge(options.slice(:digest, :serializer))) end def extract_key(key_generator: nil, secret: nil, salt:) diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index 17baf3550b..915038c854 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -121,11 +121,23 @@ class MessageEncryptorTest < ActiveSupport::TestCase old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw key") encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") - encryptor.rotate raw_key: old_raw_key, cipher: "aes-256-gcm" + encryptor.rotate raw_key: old_raw_key assert_equal "message encrypted with old raw key", encryptor.decrypt_and_verify(old_message) end + def test_rotating_serializer + old_raw_key = SecureRandom.random_bytes(32) + + old_message = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm", serializer: JSON). + encrypt_and_sign(ahoy: :hoy) + + encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm", serializer: JSON) + encryptor.rotate raw_key: old_raw_key + + assert_equal({ "ahoy" => "hoy" }, encryptor.decrypt_and_verify(old_message)) + end + def test_with_rotated_secret_and_salt old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt, 32) @@ -134,7 +146,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase old_message = old_encryptor.encrypt_and_sign("message encrypted with old secret and salt") encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") - encryptor.rotate secret: old_secret, salt: old_salt, cipher: "aes-256-gcm" + encryptor.rotate secret: old_secret, salt: old_salt assert_equal "message encrypted with old secret and salt", encryptor.decrypt_and_verify(old_message) end @@ -147,7 +159,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase old_message = old_encryptor.encrypt_and_sign("message encrypted with old key generator and salt") encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") - encryptor.rotate key_generator: old_key_gen, salt: old_salt, cipher: "aes-256-gcm" + encryptor.rotate key_generator: old_key_gen, salt: old_salt assert_equal "message encrypted with old key generator and salt", encryptor.decrypt_and_verify(old_message) end @@ -227,7 +239,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase "message encrypted with old secret, salt, and metadata", purpose: "rotation") encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") - encryptor.rotate secret: old_secret, salt: old_salt, cipher: "aes-256-gcm" + encryptor.rotate secret: old_secret, salt: old_salt assert_equal "message encrypted with old secret, salt, and metadata", encryptor.decrypt_and_verify(old_message, purpose: "rotation") diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 3079c48c02..b43fc4b269 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -99,7 +99,7 @@ class MessageVerifierTest < ActiveSupport::TestCase old_message = old_verifier.generate("message verified with old raw key") verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") - verifier.rotate raw_key: old_raw_key, digest: "SHA1" + verifier.rotate raw_key: old_raw_key assert_equal "message verified with old raw key", verifier.verified(old_message) end @@ -112,7 +112,7 @@ class MessageVerifierTest < ActiveSupport::TestCase old_message = old_verifier.generate("message verified with old secret and salt") verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") - verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1" + verifier.rotate secret: old_secret, salt: old_salt assert_equal "message verified with old secret and salt", verifier.verified(old_message) end @@ -125,7 +125,7 @@ class MessageVerifierTest < ActiveSupport::TestCase old_message = old_verifier.generate("message verified with old key generator and salt") verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") - verifier.rotate key_generator: old_key_gen, salt: old_salt, digest: "SHA1" + verifier.rotate key_generator: old_key_gen, salt: old_salt assert_equal "message verified with old key generator and salt", verifier.verified(old_message) end @@ -175,7 +175,7 @@ class MessageVerifierTest < ActiveSupport::TestCase "message verified with old secret, salt, and metadata", purpose: "rotation") verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") - verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1" + verifier.rotate secret: old_secret, salt: old_salt assert_equal "message verified with old secret, salt, and metadata", verifier.verified(old_message, purpose: "rotation") -- cgit v1.2.3 From b5aa2e0c495b310cbef90b2185ef28cd00745b23 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 24 Sep 2017 20:06:38 +0200 Subject: Remove advanced key generator rotations from verifier/encryptor. Noticed that verifiers and encryptors never once mentioned key generators and salts but only concerned themselves with generated secrets. Clears up the confusing naming around raw_key and secret as well. And makes the rotation API follow the constructor signature to the letter. --- .../lib/active_support/messages/rotator.rb | 40 +------ activesupport/test/message_encryptor_test.rb | 128 ++++++--------------- activesupport/test/message_verifier_test.rb | 96 +++++----------- 3 files changed, 66 insertions(+), 198 deletions(-) diff --git a/activesupport/lib/active_support/messages/rotator.rb b/activesupport/lib/active_support/messages/rotator.rb index e18549d735..823a399d67 100644 --- a/activesupport/lib/active_support/messages/rotator.rb +++ b/activesupport/lib/active_support/messages/rotator.rb @@ -10,8 +10,8 @@ module ActiveSupport @rotations = [] end - def rotate(*args) - @rotations << create_rotation(*args) + def rotate(*secrets, **options) + @rotations << build_rotation(*secrets, @options.merge(options)) end module Encryptor @@ -24,27 +24,8 @@ module ActiveSupport end private - def create_rotation(raw_key: nil, raw_signed_key: nil, **options) - options[:cipher] ||= @cipher - - self.class.new \ - raw_key || extract_key(options), - raw_signed_key || extract_signing_key(options), - @options.merge(options.slice(:cipher, :digest, :serializer)) - end - - def extract_key(cipher:, salt:, key_generator: nil, secret: nil, **) - key_generator ||= key_generator_for(secret) - key_generator.generate_key(salt, self.class.key_len(cipher)) - end - - def extract_signing_key(cipher:, signed_salt: nil, key_generator: nil, secret: nil, **) - if cipher.downcase.end_with?("cbc") - raise ArgumentError, "missing signed_salt for signing key generation" unless signed_salt - - key_generator ||= key_generator_for(secret) - key_generator.generate_key(signed_salt) - end + def build_rotation(secret = @secret, sign_secret = @sign_secret, options) + self.class.new(secret, sign_secret, options) end end @@ -56,21 +37,12 @@ module ActiveSupport end private - def create_rotation(raw_key: nil, **options) - self.class.new(raw_key || extract_key(options), @options.merge(options.slice(:digest, :serializer))) - end - - def extract_key(key_generator: nil, secret: nil, salt:) - key_generator ||= key_generator_for(secret) - key_generator.generate_key(salt) + def build_rotation(secret = @secret, options) + self.class.new(secret, options) end end private - def key_generator_for(secret) - ActiveSupport::KeyGenerator.new(secret, iterations: 1000) - end - def run_rotations(on_rotation) @rotations.find do |rotation| if message = yield(rotation) rescue next diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index 915038c854..8fde3928dc 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -115,134 +115,70 @@ class MessageEncryptorTest < ActiveSupport::TestCase assert_equal "Ruby on Rails", encryptor.decrypt_and_verify(encrypted_message) end - def test_with_rotated_raw_key - old_raw_key = SecureRandom.random_bytes(32) - old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm") - old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw key") + def test_rotating_secret + old_message = ActiveSupport::MessageEncryptor.new(secrets[:old], cipher: "aes-256-gcm").encrypt_and_sign("old") encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") - encryptor.rotate raw_key: old_raw_key + encryptor.rotate secrets[:old] - assert_equal "message encrypted with old raw key", encryptor.decrypt_and_verify(old_message) + assert_equal "old", encryptor.decrypt_and_verify(old_message) end def test_rotating_serializer - old_raw_key = SecureRandom.random_bytes(32) - - old_message = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm", serializer: JSON). + old_message = ActiveSupport::MessageEncryptor.new(secrets[:old], cipher: "aes-256-gcm", serializer: JSON). encrypt_and_sign(ahoy: :hoy) encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm", serializer: JSON) - encryptor.rotate raw_key: old_raw_key + encryptor.rotate secrets[:old] assert_equal({ "ahoy" => "hoy" }, encryptor.decrypt_and_verify(old_message)) end - def test_with_rotated_secret_and_salt - old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" - old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt, 32) - - old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm") - old_message = old_encryptor.encrypt_and_sign("message encrypted with old secret and salt") - - encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") - encryptor.rotate secret: old_secret, salt: old_salt - - assert_equal "message encrypted with old secret and salt", encryptor.decrypt_and_verify(old_message) - end - - def test_with_rotated_key_generator - old_key_gen, old_salt = ActiveSupport::KeyGenerator.new(SecureRandom.random_bytes(32), iterations: 256), "old salt" - - old_raw_key = old_key_gen.generate_key(old_salt, 32) - old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm") - old_message = old_encryptor.encrypt_and_sign("message encrypted with old key generator and salt") - - encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") - encryptor.rotate key_generator: old_key_gen, salt: old_salt - - assert_equal "message encrypted with old key generator and salt", encryptor.decrypt_and_verify(old_message) - end - - def test_with_rotated_aes_cbc_encryptor_with_raw_keys - old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) - - old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") - old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw keys") - - encryptor = ActiveSupport::MessageEncryptor.new(@secret) - encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" - - assert_equal "message encrypted with old raw keys", encryptor.decrypt_and_verify(old_message) - end - - def test_with_rotated_aes_cbc_encryptor_with_secret_and_salts - old_secret, old_salt, old_signed_salt = SecureRandom.random_bytes(32), "old salt", "old signed salt" - - old_key_gen = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000) - old_raw_key = old_key_gen.generate_key(old_salt, 32) - old_raw_signed_key = old_key_gen.generate_key(old_signed_salt) - - old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") - old_message = old_encryptor.encrypt_and_sign("message encrypted with old secret and salts") + def test_rotating_aes_cbc_secrets + old_encryptor = ActiveSupport::MessageEncryptor.new(secrets[:old], "old sign", cipher: "aes-256-cbc") + old_message = old_encryptor.encrypt_and_sign("old") encryptor = ActiveSupport::MessageEncryptor.new(@secret) - encryptor.rotate secret: old_secret, salt: old_salt, signed_salt: old_signed_salt, cipher: "aes-256-cbc", digest: "SHA1" + encryptor.rotate secrets[:old], "old sign", cipher: "aes-256-cbc" - assert_equal "message encrypted with old secret and salts", encryptor.decrypt_and_verify(old_message) + assert_equal "old", encryptor.decrypt_and_verify(old_message) end - def test_with_rotating_multiple_encryptors - older_raw_key, older_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) - old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) - - older_encryptor = ActiveSupport::MessageEncryptor.new(older_raw_key, older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") - older_message = older_encryptor.encrypt_and_sign("message encrypted with older raw key") - - old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") - old_message = old_encryptor.encrypt_and_sign("message encrypted with old raw key") + def test_multiple_rotations + older_message = ActiveSupport::MessageEncryptor.new(secrets[:older], "older sign").encrypt_and_sign("older") + old_message = ActiveSupport::MessageEncryptor.new(secrets[:old], "old sign").encrypt_and_sign("old") encryptor = ActiveSupport::MessageEncryptor.new(@secret) - encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" - encryptor.rotate raw_key: older_raw_key, raw_signed_key: older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" + encryptor.rotate secrets[:old], "old sign" + encryptor.rotate secrets[:older], "older sign" - assert_equal "encrypted message", encryptor.decrypt_and_verify(encryptor.encrypt_and_sign("encrypted message")) - assert_equal "message encrypted with old raw key", encryptor.decrypt_and_verify(old_message) - assert_equal "message encrypted with older raw key", encryptor.decrypt_and_verify(older_message) + assert_equal "new", encryptor.decrypt_and_verify(encryptor.encrypt_and_sign("new")) + assert_equal "old", encryptor.decrypt_and_verify(old_message) + assert_equal "older", encryptor.decrypt_and_verify(older_message) end - def test_on_rotation_instance_callback_is_called_and_returns_modified_messages - callback_ran, message = nil, nil - - older_raw_key, older_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) - old_raw_key, old_raw_signed_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(16) - - older_encryptor = ActiveSupport::MessageEncryptor.new(older_raw_key, older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1") - older_message = older_encryptor.encrypt_and_sign(encoded: "message") + def test_on_rotation_is_called_and_returns_modified_messages + older_message = ActiveSupport::MessageEncryptor.new(secrets[:older], "older sign").encrypt_and_sign(encoded: "message") encryptor = ActiveSupport::MessageEncryptor.new(@secret) - encryptor.rotate raw_key: old_raw_key, raw_signed_key: old_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" - encryptor.rotate raw_key: older_raw_key, raw_signed_key: older_raw_signed_key, cipher: "aes-256-cbc", digest: "SHA1" + encryptor.rotate secrets[:old] + encryptor.rotate secrets[:older], "older sign" - message = encryptor.decrypt_and_verify(older_message, on_rotation: proc { callback_ran = true }) + rotated = false + message = encryptor.decrypt_and_verify(older_message, on_rotation: proc { rotated = true }) - assert callback_ran, "callback was ran" assert_equal({ encoded: "message" }, message) + assert rotated end def test_with_rotated_metadata - old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" - old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt, 32) - - old_encryptor = ActiveSupport::MessageEncryptor.new(old_raw_key, cipher: "aes-256-gcm") - old_message = old_encryptor.encrypt_and_sign( - "message encrypted with old secret, salt, and metadata", purpose: "rotation") + old_message = ActiveSupport::MessageEncryptor.new(secrets[:old], cipher: "aes-256-gcm"). + encrypt_and_sign("metadata", purpose: :rotation) encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") - encryptor.rotate secret: old_secret, salt: old_salt + encryptor.rotate secrets[:old] - assert_equal "message encrypted with old secret, salt, and metadata", - encryptor.decrypt_and_verify(old_message, purpose: "rotation") + assert_equal "metadata", encryptor.decrypt_and_verify(old_message, purpose: :rotation) end private @@ -264,6 +200,10 @@ class MessageEncryptorTest < ActiveSupport::TestCase end end + def secrets + @secrets ||= Hash.new { |h,k| h[k] = SecureRandom.random_bytes(32) } + end + def munge(base64_string) bits = ::Base64.strict_decode64(base64_string) bits.reverse! diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index b43fc4b269..05d5c1cbc3 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -92,93 +92,49 @@ class MessageVerifierTest < ActiveSupport::TestCase assert_equal @data, @verifier.verify(signed_message) end - def test_with_rotated_raw_key - old_raw_key = SecureRandom.random_bytes(32) - - old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1") - old_message = old_verifier.generate("message verified with old raw key") - - verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") - verifier.rotate raw_key: old_raw_key - - assert_equal "message verified with old raw key", verifier.verified(old_message) - end - - def test_with_rotated_secret_and_salt - old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" - - old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt) - old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1") - old_message = old_verifier.generate("message verified with old secret and salt") + def test_rotating_secret + old_message = ActiveSupport::MessageVerifier.new("old", digest: "SHA1").generate("old") verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") - verifier.rotate secret: old_secret, salt: old_salt + verifier.rotate "old" - assert_equal "message verified with old secret and salt", verifier.verified(old_message) + assert_equal "old", verifier.verified(old_message) end - def test_with_rotated_key_generator - old_key_gen, old_salt = ActiveSupport::KeyGenerator.new(SecureRandom.random_bytes(32), iterations: 256), "old salt" + def test_multiple_rotations + old_message = ActiveSupport::MessageVerifier.new("old", digest: "SHA256").generate("old") + older_message = ActiveSupport::MessageVerifier.new("older", digest: "SHA1").generate("older") - old_raw_key = old_key_gen.generate_key(old_salt) - old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1") - old_message = old_verifier.generate("message verified with old key generator and salt") + verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512") + verifier.rotate "old", digest: "SHA256" + verifier.rotate "older", digest: "SHA1" - verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") - verifier.rotate key_generator: old_key_gen, salt: old_salt - - assert_equal "message verified with old key generator and salt", verifier.verified(old_message) + assert_equal "new", verifier.verified(verifier.generate("new")) + assert_equal "old", verifier.verified(old_message) + assert_equal "older", verifier.verified(older_message) end - def test_with_rotating_multiple_verifiers - old_raw_key, older_raw_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(32) - - old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA256") - old_message = old_verifier.generate("message verified with old raw key") + def test_on_rotation_is_called_and_verified_returns_message + older_message = ActiveSupport::MessageVerifier.new("older", digest: "SHA1").generate(encoded: "message") - older_verifier = ActiveSupport::MessageVerifier.new(older_raw_key, digest: "SHA1") - older_message = older_verifier.generate("message verified with older raw key") + verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512") + verifier.rotate "old", digest: "SHA256" + verifier.rotate "older", digest: "SHA1" - verifier = ActiveSupport::MessageVerifier.new("new secret", digest: "SHA512") - verifier.rotate raw_key: old_raw_key, digest: "SHA256" - verifier.rotate raw_key: older_raw_key, digest: "SHA1" + rotated = false + message = verifier.verified(older_message, on_rotation: proc { rotated = true }) - assert_equal "verified message", verifier.verified(verifier.generate("verified message")) - assert_equal "message verified with old raw key", verifier.verified(old_message) - assert_equal "message verified with older raw key", verifier.verified(older_message) - end - - def test_on_rotation_keyword_block_is_called_and_verified_returns_message - callback_ran, message = nil, nil - - old_raw_key, older_raw_key = SecureRandom.random_bytes(32), SecureRandom.random_bytes(32) - - older_verifier = ActiveSupport::MessageVerifier.new(older_raw_key, digest: "SHA1") - older_message = older_verifier.generate(encoded: "message") - - verifier = ActiveSupport::MessageVerifier.new("new secret", digest: "SHA512") - verifier.rotate raw_key: old_raw_key, digest: "SHA256" - verifier.rotate raw_key: older_raw_key, digest: "SHA1" - - message = verifier.verified(older_message, on_rotation: proc { callback_ran = true }) - - assert callback_ran, "callback was ran" assert_equal({ encoded: "message" }, message) + assert rotated end - def test_with_rotated_metadata - old_secret, old_salt = SecureRandom.random_bytes(32), "old salt" + def test_rotations_with_metadata + old_message = ActiveSupport::MessageVerifier.new("old").generate("old", purpose: :rotation) - old_raw_key = ActiveSupport::KeyGenerator.new(old_secret, iterations: 1000).generate_key(old_salt) - old_verifier = ActiveSupport::MessageVerifier.new(old_raw_key, digest: "SHA1") - old_message = old_verifier.generate( - "message verified with old secret, salt, and metadata", purpose: "rotation") - - verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA1") - verifier.rotate secret: old_secret, salt: old_salt + verifier = ActiveSupport::MessageVerifier.new(@secret) + verifier.rotate "old" - assert_equal "message verified with old secret, salt, and metadata", - verifier.verified(old_message, purpose: "rotation") + assert_equal "old", verifier.verified(old_message, purpose: :rotation) end end -- cgit v1.2.3 From 9d79d77813c3aca010a5b40cacbd6e68f42ce618 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 24 Sep 2017 21:25:59 +0200 Subject: Use new rotation signature in cookies. [ Michael Coyne & Kasper Timm Hansen ] --- .../lib/action_dispatch/middleware/cookies.rb | 23 ++++---- actionpack/test/dispatch/cookies_test.rb | 65 +++------------------- .../messages/rotation_configuration.rb | 10 ++-- 3 files changed, 25 insertions(+), 73 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index b3831649a8..06ce0b22f4 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -264,9 +264,9 @@ module ActionDispatch end def upgrade_legacy_hmac_aes_cbc_cookies? - request.secret_key_base.present? && - request.encrypted_signed_cookie_salt.present? && - request.encrypted_cookie_salt.present? && + request.secret_key_base.present? && + request.encrypted_signed_cookie_salt.present? && + request.encrypted_cookie_salt.present? && request.use_authenticated_cookie_encryption end @@ -570,12 +570,12 @@ module ActionDispatch secret = request.key_generator.generate_key(request.signed_cookie_salt) @verifier = ActiveSupport::MessageVerifier.new(secret, digest: signed_cookie_digest, serializer: SERIALIZER) - request.cookies_rotations.signed.each do |rotation_options| - @verifier.rotate serializer: SERIALIZER, **rotation_options + request.cookies_rotations.signed.each do |*secrets, **options| + @verifier.rotate *secrets, serializer: SERIALIZER, **options end if upgrade_legacy_signed_cookies? - @verifier.rotate raw_key: request.secret_token, serializer: SERIALIZER + @verifier.rotate request.secret_token, serializer: SERIALIZER end end @@ -603,14 +603,15 @@ module ActionDispatch secret = request.key_generator.generate_key(request.authenticated_encrypted_cookie_salt, key_len) @encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: encrypted_cookie_cipher, serializer: SERIALIZER) - request.cookies_rotations.encrypted.each do |rotation_options| - @encryptor.rotate serializer: SERIALIZER, **rotation_options + request.cookies_rotations.encrypted.each do |*secrets, **options| + @encryptor.rotate *secrets, serializer: SERIALIZER, **options end if upgrade_legacy_hmac_aes_cbc_cookies? - @encryptor.rotate \ - key_generator: request.key_generator, salt: request.encrypted_cookie_salt, signed_salt: request.encrypted_signed_cookie_salt, - cipher: "aes-256-cbc", digest: digest, serializer: SERIALIZER + secret = request.key_generator.generate_key(request.encrypted_cookie_salt) + sign_secret = request.key_generator.generate_key(request.encrypted_signed_cookie_salt) + + @encryptor.rotate secret, sign_secret, cipher: "aes-256-cbc", digest: digest, serializer: SERIALIZER end if upgrade_legacy_signed_cookies? diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 706d0be9c2..70587fa2b0 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -461,37 +461,13 @@ class CookiesTest < ActionController::TestCase assert_equal verifier.generate(45), cookies[:user_id] end - def test_signed_cookie_rotations_with_secret_key_base_and_digest - rotated_secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" - rotated_salt = "signed cookie" + def test_signed_cookie_rotating_secret_and_digest + secret = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.signed_cookie_digest"] = "SHA256" - @request.env["action_dispatch.cookies_rotations"].rotate :signed, - secret: rotated_secret_key_base, salt: rotated_salt, digest: "SHA1" - - old_secret = ActiveSupport::KeyGenerator.new(rotated_secret_key_base, iterations: 1000).generate_key(rotated_salt) - old_message = ActiveSupport::MessageVerifier.new(old_secret, digest: "SHA1", serializer: Marshal).generate(45) - - @request.headers["Cookie"] = "user_id=#{old_message}" - - get :get_signed_cookie - assert_equal 45, @controller.send(:cookies).signed[:user_id] - - key_generator = @request.env["action_dispatch.key_generator"] - secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) - verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA256", serializer: Marshal) - assert_equal 45, verifier.verify(@response.cookies["user_id"]) - end - - def test_signed_cookie_rotations_with_raw_key_and_digest - rotated_raw_key = "b3c631c314c0bbca50c1b2843150fe33" - - @request.env["action_dispatch.signed_cookie_digest"] = "SHA256" - @request.env["action_dispatch.cookies_rotations"].rotate :signed, - raw_key: rotated_raw_key, digest: "SHA1" - - old_message = ActiveSupport::MessageVerifier.new(rotated_raw_key, digest: "SHA1", serializer: Marshal).generate(45) + @request.env["action_dispatch.cookies_rotations"].rotate :signed, secret, digest: "SHA1" + old_message = ActiveSupport::MessageVerifier.new(secret, digest: "SHA1", serializer: Marshal).generate(45) @request.headers["Cookie"] = "user_id=#{old_message}" get :get_signed_cookie @@ -993,40 +969,15 @@ class CookiesTest < ActionController::TestCase assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) end - def test_encrypted_cookie_rotations_with_secret_and_salt - rotated_secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" - rotated_salt = "authenticated encrypted cookie" - - @request.env["action_dispatch.encrypted_cookie_cipher"] = "aes-256-gcm" - @request.env["action_dispatch.cookies_rotations"].rotate :encrypted, - secret: rotated_secret_key_base, salt: rotated_salt, cipher: "aes-256-gcm" - - key_len = ActiveSupport::MessageEncryptor.key_len("aes-256-gcm") - - old_secret = ActiveSupport::KeyGenerator.new(rotated_secret_key_base, iterations: 1000).generate_key(rotated_salt, key_len) - old_message = ActiveSupport::MessageEncryptor.new(old_secret, cipher: "aes-256-gcm", serializer: Marshal).encrypt_and_sign("bar") - - @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape old_message}" - - get :get_encrypted_cookie - assert_equal "bar", @controller.send(:cookies).encrypted[:foo] - - key_generator = @request.env["action_dispatch.key_generator"] - secret = key_generator.generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], key_len) - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal) - assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) - end - - def test_encrypted_cookie_rotations_with_raw_key - raw_key = "b3c631c314c0bbca50c1b2843150fe33" + def test_encrypted_cookie_rotating_secret + secret = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.encrypted_cookie_cipher"] = "aes-256-gcm" - @request.env["action_dispatch.cookies_rotations"].rotate :encrypted, - raw_key: raw_key, cipher: "aes-256-gcm" + @request.env["action_dispatch.cookies_rotations"].rotate :encrypted, secret key_len = ActiveSupport::MessageEncryptor.key_len("aes-256-gcm") - old_message = ActiveSupport::MessageEncryptor.new(raw_key, cipher: "aes-256-gcm", serializer: Marshal).encrypt_and_sign(45) + old_message = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal).encrypt_and_sign(45) @request.headers["Cookie"] = "foo=#{::Rack::Utils.escape old_message}" diff --git a/activesupport/lib/active_support/messages/rotation_configuration.rb b/activesupport/lib/active_support/messages/rotation_configuration.rb index 908658ff02..233703b558 100644 --- a/activesupport/lib/active_support/messages/rotation_configuration.rb +++ b/activesupport/lib/active_support/messages/rotation_configuration.rb @@ -9,15 +9,15 @@ module ActiveSupport @signed, @encrypted = [], [] end - def rotate(kind = nil, **options) + def rotate(kind = nil, *args) case kind when :signed - @signed << options + @signed << args when :encrypted - @encrypted << options + @encrypted << args else - rotate :signed, options - rotate :encrypted, options + rotate :signed, args + rotate :encrypted, args end end end -- cgit v1.2.3 From 38308e6d1353eda587d676ac40ce489c638fb0c3 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 24 Sep 2017 21:41:16 +0200 Subject: [ci skip] Attempt a new explanation for rotations. It's become clear to me that the use case is still a bit muddy and the upgrade path is going to be tough for people to figure out. This attempts at understanding it better through documentation, but still needs follow up work. [ Michael Coyne & Kasper Timm Hansen ] --- .../lib/active_support/message_encryptor.rb | 46 ++++++++++------------ .../lib/active_support/message_verifier.rb | 45 ++++++++++----------- guides/source/configuring.md | 6 +-- guides/source/security.md | 44 ++++++++------------- 4 files changed, 60 insertions(+), 81 deletions(-) diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 003fb4c354..8a1918039c 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -57,31 +57,27 @@ module ActiveSupport # # === Rotating keys # - # This class also defines a +rotate+ method which can be used to rotate out - # encryption keys no longer in use. - # - # This method is called with an options hash where a +:cipher+ option and - # either a +:raw_key+ or +:secret+ option must be defined. If +:raw_key+ is - # defined, it is used directly for the underlying encryption function. If - # the +:secret+ option is defined, a +:salt+ option must also be defined and - # a +KeyGenerator+ instance will be used to derive a key using +:salt+. When - # +:secret+ is used, a +:key_generator+ option may also be defined allowing - # for custom +KeyGenerator+ instances. If CBC encryption is used a - # `:raw_signed_key` or a `:signed_salt` option must also be defined. A - # +:digest+ may also be defined when using CBC encryption. This method can be - # called multiple times and new encryptor instances will be added to the - # rotation stack on each call. - # - # # Specifying the key used for encryption - # crypt.rotate raw_key: old_aead_key, cipher: "aes-256-gcm" - # crypt.rotate raw_key: old_cbc_key, raw_signed_key: old_cbc_sign_key, cipher: "aes-256-cbc", digest: "SHA1" - # - # # Using a KeyGenerator instance with a secret and salt(s) - # crypt.rotate secret: old_aead_secret, salt: old_aead_salt, cipher: "aes-256-gcm" - # crypt.rotate secret: old_cbc_secret, salt: old_cbc_salt, signed_salt: old_cbc_signed_salt, cipher: "aes-256-cbc", digest: "SHA1" - # - # # Specifying the key generator instance - # crypt.rotate key_generator: old_key_gen, salt: old_salt, cipher: "aes-256-gcm" + # MessageEncryptor also supports rotating out old configurations by falling + # back to a stack of encryptors. Call `rotate` to build and add an encryptor + # so `decrypt_and_verify` will also try the fallback. + # + # By default any rotated encryptors use the values of the primary + # encryptor unless specified otherwise. + # + # You'd give your encryptor the new defaults: + # + # crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm") + # + # Then gradually rotate the old values out by adding them as fallbacks. Any message + # generated with the old values will then work until the rotation is removed. + # + # crypt.rotate old_secret # Fallback to an old secret instead of @secret. + # crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm. + # + # Though if both the secret and the cipher was changed at the same time, + # the above should be combined into: + # + # verifier.rotate old_secret, cipher: "aes-256-cbc" class MessageEncryptor prepend Messages::Rotator::Encryptor diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 0be13f6f03..f0b6503b96 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -77,30 +77,27 @@ module ActiveSupport # # === Rotating keys # - # This class also defines a +rotate+ method which can be used to rotate out - # verification keys no longer in use. - # - # This method is called with an options hash where a +:digest+ option and - # either a +:raw_key+ or +:secret+ option must be defined. If +:raw_key+ is - # defined, it is used directly for the underlying HMAC function. If the - # +:secret+ option is defined, a +:salt+ option must also be defined and a - # +KeyGenerator+ instance will be used to derive a key using +:salt+. When - # +:secret+ is used, a +:key_generator+ option may also be defined allowing - # for custom +KeyGenerator+ instances. This method can be called multiple - # times and new verifier instances will be added to the rotation stack on - # each call. - # - # # Specifying the key used for verification - # @verifier.rotate raw_key: older_key, digest: "SHA1" - # - # # Specify the digest - # @verifier.rotate raw_key: old_key, digest: "SHA256" - # - # # Using a KeyGenerator instance with a secret and salt - # @verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1" - # - # # Specifying the key generator instance - # @verifier.rotate key_generator: old_key_gen, salt: old_salt, digest: "SHA256" + # MessageVerifier also supports rotating out old configurations by falling + # back to a stack of verifiers. Call `rotate` to build and add a verifier to + # so either `verified` or `verify` will also try verifying with the fallback. + # + # By default any rotated verifiers use the values of the primary + # verifier unless specified otherwise. + # + # You'd give your verifier the new defaults: + # + # verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512", serializer: JSON) + # + # Then gradually rotate the old values out by adding them as fallbacks. Any message + # generated with the old values will then work until the rotation is removed. + # + # verifier.rotate old_secret # Fallback to an old secret instead of @secret. + # verifier.rotate digest: "SHA256" # Fallback to an old digest instead of SHA512. + # verifier.rotate serializer: Marshal # Fallback to an old serializer instead of JSON. + # + # Though the above would most likely be combined into one rotation: + # + # verifier.rotate old_secret, digest: "SHA256", serializer: Marshal class MessageVerifier prepend Messages::Rotator::Verifier diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 86c8364d83..0f87d73d6e 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -493,10 +493,8 @@ Defaults to `'signed cookie'`. * `config.action_dispatch.signed_cookie_digest` sets the digest to be used for signed cookies. This defaults to `"SHA1"`. -* `config.action_dispatch.cookies_rotations` is set to an instance of - [RotationConfiguration](http://api.rubyonrails.org/classes/ActiveSupport/RotationConfiguration.html). - It provides an interface for rotating keys, salts, ciphers, and - digests for encrypted and signed cookies. +* `config.action_dispatch.cookies_rotations` allows rotating + secrets, ciphers, and digests for encrypted and signed cookies. * `config.action_dispatch.perform_deep_munge` configures whether `deep_munge` method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation) diff --git a/guides/source/security.md b/guides/source/security.md index 994978b88b..9e1dc518d2 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -161,43 +161,31 @@ It is also useful to rotate this value for other more benign reasons, such as an employee leaving your organization or changing hosting environments. -Key rotations can be defined through -`config.action_dispatch.cookies_rotations` which provides an interface for -rotating signed and encrypted cookie keys, salts, digests, and ciphers. - -For example, suppose we want to rotate out an old `secret_key_base`, we -can define a signed and encrypted key rotation as follows: +For example to rotate out an old `secret_key_base`, we can define signed and +encrypted rotations as follows: ```ruby -config.action_dispatch.cookies_rotations.rotate :encrypted, - cipher: "aes-256-gcm", - secret: Rails.application.credentials.old_secret_key_base, - salt: config.action_dispatch.authenticated_encrypted_cookie_salt - -config.action_dispatch.cookies_rotations.rotate :signed, - digest: "SHA1", - secret: Rails.application.credentials.old_secret_key_base, - salt: config.action_dispatch.signed_cookie_salt +Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| + cookies.rotate :encrypted, secret: Rails.application.credentials.old_secret_key_base + cookies.rotate :signed, secret: Rails.application.credentials.old_secret_key_base +end ``` -Multiple rotations are possible by calling `rotate` multiple times. For -example, suppose we want to use SHA512 for signed cookies while rotating -out SHA256 and SHA1 digests using the same `secret_key_base`: +It's also possible to set up multiple rotations. For instance to use `SHA512` +for signed cookies while rotating out SHA256 and SHA1 digests, we'd do: ```ruby -config.action_dispatch.signed_cookie_digest = "SHA512" +Rails.application.config.action_dispatch.signed_cookie_digest = "SHA512" -config.action_dispatch.cookies_rotations.rotate :signed, - digest: "SHA256", - secret: Rails.application.credentials.secret_key_base, - salt: config.action_dispatch.signed_cookie_salt - -config.action_dispatch.cookies_rotations.rotate :signed, - digest: "SHA1", - secret: Rails.application.credentials.secret_key_base, - salt: config.action_dispatch.signed_cookie_salt +Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| + cookies.rotate :signed, digest: "SHA256" + cookies.rotate :signed, digest: "SHA1" +end ``` +While you can setup as many rotations as you'd like it's not common to have many +rotations going at any one time. + For more details on key rotation with encrypted and signed messages as well as the various options the `rotate` method accepts, please refer to the -- cgit v1.2.3 From 90458751675654d734b8f999ff2c0059db59fdf0 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sun, 24 Sep 2017 22:24:45 +0300 Subject: Remove unused variable `gem_version` from `tasks/release.rb` --- tasks/release.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/tasks/release.rb b/tasks/release.rb index aa8ba44c1a..6ff06f3c4a 100644 --- a/tasks/release.rb +++ b/tasks/release.rb @@ -6,7 +6,6 @@ FRAMEWORK_NAMES = Hash.new { |h, k| k.split(/(?<=active|action)/).map(&:capitali root = File.expand_path("..", __dir__) version = File.read("#{root}/RAILS_VERSION").strip tag = "v#{version}" -gem_version = Gem::Version.new(version) directory "pkg" -- cgit v1.2.3 From 8b1fe28017b264de770e15f2417e84ef57ae0571 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 25 Sep 2017 05:31:08 +0900 Subject: Fix "warning: `*' interpreted as argument prefix" --- actionpack/lib/action_dispatch/middleware/cookies.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 06ce0b22f4..baffe200bc 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -571,7 +571,7 @@ module ActionDispatch @verifier = ActiveSupport::MessageVerifier.new(secret, digest: signed_cookie_digest, serializer: SERIALIZER) request.cookies_rotations.signed.each do |*secrets, **options| - @verifier.rotate *secrets, serializer: SERIALIZER, **options + @verifier.rotate(*secrets, serializer: SERIALIZER, **options) end if upgrade_legacy_signed_cookies? @@ -604,7 +604,7 @@ module ActionDispatch @encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: encrypted_cookie_cipher, serializer: SERIALIZER) request.cookies_rotations.encrypted.each do |*secrets, **options| - @encryptor.rotate *secrets, serializer: SERIALIZER, **options + @encryptor.rotate(*secrets, serializer: SERIALIZER, **options) end if upgrade_legacy_hmac_aes_cbc_cookies? -- cgit v1.2.3 From f9a6c00dcc95e9c9f5c064913f0a6b65fd9655a6 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 24 Sep 2017 22:58:17 +0200 Subject: Fix RotationConfiguration test and remove nil-kind rotates. --- .../messages/rotation_configuration.rb | 5 +--- .../test/messages/rotation_configuration_test.rb | 32 +++++----------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/activesupport/lib/active_support/messages/rotation_configuration.rb b/activesupport/lib/active_support/messages/rotation_configuration.rb index 233703b558..bd50d6d348 100644 --- a/activesupport/lib/active_support/messages/rotation_configuration.rb +++ b/activesupport/lib/active_support/messages/rotation_configuration.rb @@ -9,15 +9,12 @@ module ActiveSupport @signed, @encrypted = [], [] end - def rotate(kind = nil, *args) + def rotate(kind, *args) case kind when :signed @signed << args when :encrypted @encrypted << args - else - rotate :signed, args - rotate :encrypted, args end end end diff --git a/activesupport/test/messages/rotation_configuration_test.rb b/activesupport/test/messages/rotation_configuration_test.rb index 41d938e119..2f6824ed21 100644 --- a/activesupport/test/messages/rotation_configuration_test.rb +++ b/activesupport/test/messages/rotation_configuration_test.rb @@ -9,35 +9,17 @@ class MessagesRotationConfiguration < ActiveSupport::TestCase end def test_signed_configurations - @config.rotate :signed, secret: "older secret", salt: "salt", digest: "SHA1" - @config.rotate :signed, secret: "old secret", salt: "salt", digest: "SHA256" + @config.rotate :signed, "older secret", salt: "salt", digest: "SHA1" + @config.rotate :signed, "old secret", salt: "salt", digest: "SHA256" - assert_equal [{ - secret: "older secret", salt: "salt", digest: "SHA1" - }, { - secret: "old secret", salt: "salt", digest: "SHA256" - }], @config.signed + assert_equal [ + [ "older secret", salt: "salt", digest: "SHA1" ], + [ "old secret", salt: "salt", digest: "SHA256" ] ], @config.signed end def test_encrypted_configurations - @config.rotate :encrypted, raw_key: "old raw key", cipher: "aes-256-gcm" + @config.rotate :encrypted, "old raw key", cipher: "aes-256-gcm" - assert_equal [{ - raw_key: "old raw key", cipher: "aes-256-gcm" - }], @config.encrypted - end - - def test_rotate_without_kind - @config.rotate secret: "older secret", salt: "salt", digest: "SHA1" - @config.rotate raw_key: "old raw key", cipher: "aes-256-gcm" - - expected = [{ - secret: "older secret", salt: "salt", digest: "SHA1" - }, { - raw_key: "old raw key", cipher: "aes-256-gcm" - }] - - assert_equal expected, @config.encrypted - assert_equal expected, @config.signed + assert_equal [ [ "old raw key", cipher: "aes-256-gcm" ] ], @config.encrypted end end -- cgit v1.2.3 From 0c4dc1639c692c3b4a7444409baf150bc42b717f Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 24 Sep 2017 23:07:09 +0200 Subject: Skip complex cookie tests for now; I'll deal with them tomorrow. --- railties/test/application/middleware/cookies_test.rb | 4 ++++ railties/test/application/middleware/session_test.rb | 2 ++ 2 files changed, 6 insertions(+) diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb index 932a6d0e77..092f7a1099 100644 --- a/railties/test/application/middleware/cookies_test.rb +++ b/railties/test/application/middleware/cookies_test.rb @@ -52,6 +52,8 @@ module ApplicationTests end test "signed cookies with SHA512 digest and rotated out SHA256 and SHA1 digests" do + skip "@kaspth will fix this" + key_gen_sha1 = ActiveSupport::KeyGenerator.new("legacy sha1 secret", iterations: 1000) key_gen_sha256 = ActiveSupport::KeyGenerator.new("legacy sha256 secret", iterations: 1000) @@ -120,6 +122,8 @@ module ApplicationTests end test "encrypted cookies with multiple rotated out ciphers" do + skip "@kaspth will fix this" + key_gen_one = ActiveSupport::KeyGenerator.new("legacy secret one", iterations: 1000) key_gen_two = ActiveSupport::KeyGenerator.new("legacy secret two", iterations: 1000) diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index a17988235a..36d1bf5bf2 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -301,6 +301,8 @@ module ApplicationTests end test "session upgrading from AES-CBC-HMAC encryption to AES-GCM encryption" do + skip "@kaspth will fix this" + app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do get ':controller(/:action)' -- cgit v1.2.3 From 3ab5f475e0014f2d449bf3f26cac429bdd5f0069 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 25 Sep 2017 07:15:52 +0900 Subject: Fix `test_should_sanitize_illegal_style_properties` failure https://travis-ci.org/rails/rails/jobs/279300966#L2600 The result of `Loofah::HTML5::Scrub.scrub_css` was changed since v2.1.0.rc1. https://github.com/flavorjones/loofah/commit/ca56295ff9e802018ea18d23ed49be235a95ccad --- actionview/test/template/sanitize_helper_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionview/test/template/sanitize_helper_test.rb b/actionview/test/template/sanitize_helper_test.rb index c7714cf205..0e690c82cb 100644 --- a/actionview/test/template/sanitize_helper_test.rb +++ b/actionview/test/template/sanitize_helper_test.rb @@ -21,8 +21,8 @@ class SanitizeHelperTest < ActionView::TestCase def test_should_sanitize_illegal_style_properties raw = %(display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;) - expected = %(display: block; width: 100%; height: 100%; background-color: black; background-x: center; background-y: center;) - assert_equal expected, sanitize_css(raw) + expected = %r(\Adisplay:\s?block;\s?width:\s?100%;\s?height:\s?100%;\s?background-color:\s?black;\s?background-x:\s?center;\s?background-y:\s?center;\z) + assert_match expected, sanitize_css(raw) end def test_strip_tags -- cgit v1.2.3 From 8f037a5ab929f929fe0897edc9ecadf901c32e56 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 25 Sep 2017 08:27:41 +0900 Subject: Extract `integer_like_primary_key_type` to ease to handle it for adapters --- .../connection_adapters/abstract/schema_definitions.rb | 7 +++++++ .../connection_adapters/mysql/schema_definitions.rb | 10 +++++----- .../connection_adapters/postgresql/schema_definitions.rb | 9 +++------ .../connection_adapters/sqlite3/schema_definitions.rb | 9 +++------ 4 files changed, 18 insertions(+), 17 deletions(-) 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 1041db0b8f..be2f625d74 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -396,6 +396,9 @@ module ActiveRecord alias :belongs_to :references def new_column_definition(name, type, **options) # :nodoc: + if integer_like_primary_key?(type, options) + type = integer_like_primary_key_type(type, options) + end type = aliased_types(type.to_s, type) options[:primary_key] ||= type == :primary_key options[:null] = false if options[:primary_key] @@ -414,6 +417,10 @@ module ActiveRecord def integer_like_primary_key?(type, options) options[:primary_key] && [:integer, :bigint].include?(type) && !options.key?(:default) end + + def integer_like_primary_key_type(type, options) + type + end end class AlterTable # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb index eff96af87a..da25e4863c 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb @@ -57,17 +57,12 @@ module ActiveRecord include ColumnMethods def new_column_definition(name, type, **options) # :nodoc: - if integer_like_primary_key?(type, options) - options[:auto_increment] = true - end - case type when :virtual type = options[:type] when :primary_key type = :integer options[:limit] ||= 8 - options[:auto_increment] = true options[:primary_key] = true when /\Aunsigned_(?.+)\z/ type = $~[:type].to_sym @@ -81,6 +76,11 @@ module ActiveRecord def aliased_types(name, fallback) fallback end + + def integer_like_primary_key_type(type, options) + options[:auto_increment] = true + type + end end class Table < ActiveRecord::ConnectionAdapters::Table diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb index cb13f9fec1..75622eb304 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb @@ -179,17 +179,14 @@ module ActiveRecord class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition include ColumnMethods - def new_column_definition(name, type, **options) # :nodoc: - if integer_like_primary_key?(type, options) - type = if type == :bigint || options[:limit] == 8 + private + def integer_like_primary_key_type(type, options) + if type == :bigint || options[:limit] == 8 :bigserial else :serial end end - - super - end end class Table < ActiveRecord::ConnectionAdapters::Table diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb index 2010de1ce2..c9855019c1 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb @@ -9,13 +9,10 @@ module ActiveRecord end alias :belongs_to :references - def new_column_definition(name, type, **options) # :nodoc: - if integer_like_primary_key?(type, options) - type = :primary_key + private + def integer_like_primary_key_type(type, options) + :primary_key end - - super - end end end end -- cgit v1.2.3 From 40e495c13ccd3e99b070f6471b409762dd56d990 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 25 Sep 2017 08:57:43 +0900 Subject: Unneeded Mocha stubs for Kernel#system It's done inside each test via assert_called_with or Kernel.expects --- activerecord/test/cases/tasks/postgresql_rake_test.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb index 6302e84884..ca1defa332 100644 --- a/activerecord/test/cases/tasks/postgresql_rake_test.rb +++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb @@ -229,7 +229,6 @@ if current_adapter?(:PostgreSQLAdapter) ActiveRecord::Base.stubs(:connection).returns(@connection) ActiveRecord::Base.stubs(:establish_connection).returns(true) - Kernel.stubs(:system) end def teardown @@ -333,7 +332,6 @@ if current_adapter?(:PostgreSQLAdapter) ActiveRecord::Base.stubs(:connection).returns(@connection) ActiveRecord::Base.stubs(:establish_connection).returns(true) - Kernel.stubs(:system) end def test_structure_load -- cgit v1.2.3 From ab08f33f312c20638b9a7c38ee6318727c7f107d Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 25 Sep 2017 09:12:58 +0900 Subject: mocha 1.3.0 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index c60241d447..96207e022f 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,7 @@ gem "rake", ">= 11.1" # This needs to be with require false to ensure correct loading order, as it has to # be loaded after loading the test library. -gem "mocha", "~> 0.14", require: false +gem "mocha", require: false gem "capybara", "~> 2.15" diff --git a/Gemfile.lock b/Gemfile.lock index fb18fdc14e..938b4a71cc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -309,7 +309,7 @@ GEM path_expander (~> 1.0) minitest-server (1.0.4) minitest (~> 5.0) - mocha (0.14.0) + mocha (1.3.0) metaclass (~> 0.0.1) mono_logger (1.1.0) msgpack (1.1.0) @@ -506,7 +506,7 @@ DEPENDENCIES listen (>= 3.0.5, < 3.2) mini_magick minitest-bisect - mocha (~> 0.14) + mocha mysql2 (>= 0.4.4) nokogiri (>= 1.6.8) pg (>= 0.18.0) -- cgit v1.2.3 From 229f5361232d66efcc3a77381f0055764540c761 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 25 Sep 2017 07:10:05 +0900 Subject: Respect quiet option in all process of `rails new` command If specify the `quiet` option, expect that no status will be shown. However, some process show status. This suppresses all status output. --- .../rails/generators/rails/app/app_generator.rb | 6 ++--- .../rails/master_key/master_key_generator.rb | 26 +++++++++++----------- railties/test/generators/app_generator_test.rb | 7 +++++- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index ac82ff6633..23fdf03b05 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -69,7 +69,7 @@ module Rails def version_control if !options[:skip_git] && !options[:pretend] - run "git init" + run "git init", capture: options[:quiet] end end @@ -164,7 +164,7 @@ module Rails require_relative "../master_key/master_key_generator" after_bundle do - Rails::Generators::MasterKeyGenerator.new.add_master_key_file + Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet]).add_master_key_file end end @@ -174,7 +174,7 @@ module Rails require_relative "../credentials/credentials_generator" after_bundle do - Rails::Generators::CredentialsGenerator.new.add_credentials_file_silently + Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently end end diff --git a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb index e49d3b39e0..395687974a 100644 --- a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb +++ b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb @@ -13,15 +13,15 @@ module Rails unless MASTER_KEY_PATH.exist? key = ActiveSupport::EncryptedFile.generate_key - say "Adding #{MASTER_KEY_PATH} to store the master encryption key: #{key}" - say "" - say "Save this in a password manager your team can access." - say "" - say "If you lose the key, no one, including you, can access anything encrypted with it." + log "Adding #{MASTER_KEY_PATH} to store the master encryption key: #{key}" + log "" + log "Save this in a password manager your team can access." + log "" + log "If you lose the key, no one, including you, can access anything encrypted with it." - say "" + log "" create_file MASTER_KEY_PATH, key - say "" + log "" ignore_master_key_file end @@ -31,15 +31,15 @@ module Rails def ignore_master_key_file if File.exist?(".gitignore") unless File.read(".gitignore").include?(key_ignore) - say "Ignoring #{MASTER_KEY_PATH} so it won't end up in Git history:" - say "" + log "Ignoring #{MASTER_KEY_PATH} so it won't end up in Git history:" + log "" append_to_file ".gitignore", key_ignore - say "" + log "" end else - say "IMPORTANT: Don't commit #{MASTER_KEY_PATH}. Add this to your ignore file:" - say key_ignore, :on_green - say "" + log "IMPORTANT: Don't commit #{MASTER_KEY_PATH}. Add this to your ignore file:" + log key_ignore, :on_green + log "" end end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 904e2a5c84..20f593f25c 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -560,6 +560,11 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_no_match(/run git init/, output) end + def test_quiet_option + output = run_generator [File.join(destination_root, "myapp"), "--quiet"] + assert_empty output + end + def test_application_name_with_spaces path = File.join(destination_root, "foo bar") @@ -737,7 +742,7 @@ class AppGeneratorTest < Rails::Generators::TestCase sequence = ["git init", "install", "exec spring binstub --all", "echo ran after_bundle"] @sequence_step ||= 0 - ensure_bundler_first = -> command do + ensure_bundler_first = -> command, options = nil do assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" @sequence_step += 1 end -- cgit v1.2.3 From 21b975cfd9c725c1fb0ad9a10709f4bf757c3e41 Mon Sep 17 00:00:00 2001 From: dixpac Date: Mon, 25 Sep 2017 14:00:07 +0200 Subject: Fix minor CodeClimate issue --- activesupport/test/message_encryptor_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index 8fde3928dc..9edf07f762 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -201,7 +201,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase end def secrets - @secrets ||= Hash.new { |h,k| h[k] = SecureRandom.random_bytes(32) } + @secrets ||= Hash.new { |h, k| h[k] = SecureRandom.random_bytes(32) } end def munge(base64_string) -- cgit v1.2.3 From 5657280a628ca52a27df5c35d35049ab27b61ae7 Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Mon, 25 Sep 2017 15:05:17 +0300 Subject: new missing backquotes [ci skip] --- guides/source/engines.md | 2 +- guides/source/plugins.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/guides/source/engines.md b/guides/source/engines.md index c7331b6ca4..343c224a7c 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -1322,7 +1322,7 @@ engine. Assets within an engine work in an identical way to a full application. Because the engine class inherits from `Rails::Engine`, the application will know to -look up assets in the engine's 'app/assets' and 'lib/assets' directories. +look up assets in the engine's `app/assets` and `lib/assets` directories. Like all of the other components of an engine, the assets should be namespaced. This means that if you have an asset called `style.css`, it should be placed at diff --git a/guides/source/plugins.md b/guides/source/plugins.md index b3a7f544f5..5048444cb2 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -359,7 +359,7 @@ When you run `bin/test`, you should see the tests all pass: ### Add an Instance Method -This plugin will add a method named 'squawk' to any Active Record object that calls 'acts_as_yaffle'. The 'squawk' +This plugin will add a method named 'squawk' to any Active Record object that calls `acts_as_yaffle`. The 'squawk' method will simply set the value of one of the fields in the database. To start out, write a failing test that shows the behavior you'd like: @@ -392,7 +392,7 @@ end ``` Run the test to make sure the last two tests fail with an error that contains "NoMethodError: undefined method `squawk'", -then update 'acts_as_yaffle.rb' to look like this: +then update `acts_as_yaffle.rb` to look like this: ```ruby # yaffle/lib/yaffle/acts_as_yaffle.rb -- cgit v1.2.3 From 89711d99b08b5ace8aa5b7394e2ccedb302d118f Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Mon, 25 Sep 2017 20:29:43 +0800 Subject: PERF: Restore memoization when preloading associations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Benchmark Script ``` require 'active_record' require 'benchmark/ips' require 'ruby-prof' require 'memory_profiler' require 'byebug' ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL')) ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name, :email t.integer :topic_id t.timestamps null: false end create_table :topics, force: true do |t| t.string :title t.timestamps null: false end end attributes = { name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', email: 'foobar@email.com' } class Topic < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :topic end 100.times do User.create!(attributes) end users = User.first(50) Topic.create!(title: 'This is a topic', users: users) Benchmark.ips do |x| x.config(time: 10, warmup: 5) x.report("preload") do User.includes(:topic).all.to_a end end ``` Before ``` Calculating ------------------------------------- preload 26.000 i/100ms ------------------------------------------------- preload 265.347 (± 3.0%) i/s - 2.652k ``` After ``` Calculating ------------------------------------- preload 39.000 i/100ms ------------------------------------------------- preload 406.053 (± 1.7%) i/s - 4.095k ``` --- activerecord/lib/active_record/reflection.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 82ab2415e1..032df925bf 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -425,7 +425,6 @@ module ActiveRecord def initialize(name, scope, options, active_record) super - @automatic_inverse_of = nil @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type") @foreign_type = options[:foreign_type] || "#{name}_type" @constructable = calculate_constructable(macro, options) @@ -609,12 +608,14 @@ module ActiveRecord # If it cannot find a suitable inverse association name, it returns # +nil+. def inverse_name - options.fetch(:inverse_of) do - @automatic_inverse_of ||= automatic_inverse_of + unless defined?(@inverse_name) + @inverse_name = options.fetch(:inverse_of) { automatic_inverse_of } end + + @inverse_name end - # returns either false or the inverse association name that it finds. + # returns either +nil+ or the inverse association name that it finds. def automatic_inverse_of if can_find_inverse_of_automatically?(self) inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym @@ -631,8 +632,6 @@ module ActiveRecord return inverse_name end end - - false end # Checks if the inverse reflection that is returned from the -- cgit v1.2.3 From ff56fdb08d643b8d9892763c4b9731e0391260fd Mon Sep 17 00:00:00 2001 From: Francesco Rodriguez Date: Mon, 25 Sep 2017 14:06:15 +0200 Subject: Preload digest/sha2 to avoid thread safe error. I got this error in production using Puma in multi-threaded mode: ``` RuntimeError: Digest::Base cannot be directly inherited in Ruby from active_support/security_utils.rb:23:in `variable_size_secure_compare' from active_support/security_utils.rb:23:in `hexdigest' from active_support/security_utils.rb:23:in `digest' ``` Looks like Digest uses const_missing to load Digest::SHA256 (https://github.com/ruby/ruby/blob/trunk/ext/digest/lib/digest.rb#L8) - https://bugs.ruby-lang.org/issues/9494 - https://github.com/ruby/ruby/commit/c02fa39463a0c6bf698b01bc610135604aca2ff4 --- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 +- activesupport/lib/active_support/security_utils.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 c9607df28c..4f0c1890be 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -2,7 +2,7 @@ require_relative "../../migration/join_table" require "active_support/core_ext/string/access" -require "digest" +require "digest/sha2" module ActiveRecord module ConnectionAdapters # :nodoc: diff --git a/activesupport/lib/active_support/security_utils.rb b/activesupport/lib/active_support/security_utils.rb index 51870559ec..b6b31ef140 100644 --- a/activesupport/lib/active_support/security_utils.rb +++ b/activesupport/lib/active_support/security_utils.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "digest" +require "digest/sha2" module ActiveSupport module SecurityUtils -- cgit v1.2.3 From 1fa268bfa5667a0e9ddbfda243b5282c023ab9ad Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Mon, 25 Sep 2017 20:28:26 +0200 Subject: Fix cookies/session tests broken after merging key rotation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on, yet closes https://github.com/rails/rails/pull/30708 Fix the session test by properly truncating the legacy encryption key for cbc encryption. Borrowed straight from 👆. Fix the cookies test a little differently than the PR. Basically keep every config within the config block. [ Michael Coyne & Kasper Timm Hansen ] --- .../lib/action_dispatch/middleware/cookies.rb | 5 +- .../test/application/middleware/cookies_test.rb | 64 +++++++++++----------- .../test/application/middleware/session_test.rb | 2 - 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index baffe200bc..0213987c99 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -608,10 +608,11 @@ module ActionDispatch end if upgrade_legacy_hmac_aes_cbc_cookies? - secret = request.key_generator.generate_key(request.encrypted_cookie_salt) + legacy_cipher = "aes-256-cbc" + secret = request.key_generator.generate_key(request.encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len(legacy_cipher)) sign_secret = request.key_generator.generate_key(request.encrypted_signed_cookie_salt) - @encryptor.rotate secret, sign_secret, cipher: "aes-256-cbc", digest: digest, serializer: SERIALIZER + @encryptor.rotate(secret, sign_secret, cipher: legacy_cipher, digest: digest, serializer: SERIALIZER) end if upgrade_legacy_signed_cookies? diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb index 092f7a1099..ecb4ee3446 100644 --- a/railties/test/application/middleware/cookies_test.rb +++ b/railties/test/application/middleware/cookies_test.rb @@ -52,14 +52,6 @@ module ApplicationTests end test "signed cookies with SHA512 digest and rotated out SHA256 and SHA1 digests" do - skip "@kaspth will fix this" - - key_gen_sha1 = ActiveSupport::KeyGenerator.new("legacy sha1 secret", iterations: 1000) - key_gen_sha256 = ActiveSupport::KeyGenerator.new("legacy sha256 secret", iterations: 1000) - - verifer_sha1 = ActiveSupport::MessageVerifier.new(key_gen_sha1.generate_key("sha1 salt"), digest: :SHA1) - verifer_sha256 = ActiveSupport::MessageVerifier.new(key_gen_sha256.generate_key("sha256 salt"), digest: :SHA256) - app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do get ':controller(/:action)' @@ -72,12 +64,12 @@ module ApplicationTests protect_from_forgery with: :null_session def write_raw_cookie_sha1 - cookies[:signed_cookie] = "#{verifer_sha1.generate("signed cookie")}" + cookies[:signed_cookie] = TestVerifiers.sha1.generate("signed cookie") head :ok end def write_raw_cookie_sha256 - cookies[:signed_cookie] = "#{verifer_sha256.generate("signed cookie")}" + cookies[:signed_cookie] = TestVerifiers.sha256.generate("signed cookie") head :ok end @@ -92,44 +84,43 @@ module ApplicationTests RUBY add_to_config <<-RUBY - config.action_dispatch.cookies_rotations.rotate :signed, - digest: "SHA1", secret: "legacy sha1 secret", salt: "sha1 salt" + sha1_secret = Rails.application.key_generator.generate_key("sha1") + sha256_secret = Rails.application.key_generator.generate_key("sha256") - config.action_dispatch.cookies_rotations.rotate :signed, - digest: "SHA256", secret: "legacy sha256 secret", salt: "sha256 salt" + ::TestVerifiers = Class.new do + class_attribute :sha1, default: ActiveSupport::MessageVerifier.new(sha1_secret, digest: "SHA1") + class_attribute :sha256, default: ActiveSupport::MessageVerifier.new(sha256_secret, digest: "SHA256") + end config.action_dispatch.signed_cookie_digest = "SHA512" config.action_dispatch.signed_cookie_salt = "sha512 salt" + + config.action_dispatch.cookies_rotations.tap do |cookies| + cookies.rotate :signed, sha1_secret, digest: "SHA1" + cookies.rotate :signed, sha256_secret, digest: "SHA256" + end RUBY require "#{app_path}/config/environment" - verifer_sha512 = ActiveSupport::MessageVerifier.new(app.key_generator.generate_key("sha512 salt"), digest: :SHA512) + verifier_sha512 = ActiveSupport::MessageVerifier.new(app.key_generator.generate_key("sha512 salt"), digest: :SHA512) get "/foo/write_raw_cookie_sha1" get "/foo/read_signed" assert_equal "signed cookie".inspect, last_response.body get "/foo/read_raw_cookie" - assert_equal "signed cookie", verifer_sha512.verify(last_response.body) + assert_equal "signed cookie", verifier_sha512.verify(last_response.body) get "/foo/write_raw_cookie_sha256" get "/foo/read_signed" assert_equal "signed cookie".inspect, last_response.body get "/foo/read_raw_cookie" - assert_equal "signed cookie", verifer_sha512.verify(last_response.body) + assert_equal "signed cookie", verifier_sha512.verify(last_response.body) end - test "encrypted cookies with multiple rotated out ciphers" do - skip "@kaspth will fix this" - - key_gen_one = ActiveSupport::KeyGenerator.new("legacy secret one", iterations: 1000) - key_gen_two = ActiveSupport::KeyGenerator.new("legacy secret two", iterations: 1000) - - encryptor_one = ActiveSupport::MessageEncryptor.new(key_gen_one.generate_key("salt one", 32), cipher: "aes-256-gcm") - encryptor_two = ActiveSupport::MessageEncryptor.new(key_gen_two.generate_key("salt two", 32), cipher: "aes-256-gcm") - + test "encrypted cookies rotating multiple encryption keys" do app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do get ':controller(/:action)' @@ -142,12 +133,12 @@ module ApplicationTests protect_from_forgery with: :null_session def write_raw_cookie_one - cookies[:encrypted_cookie] = "#{encryptor_one.encrypt_and_sign("encrypted cookie")}" + cookies[:encrypted_cookie] = TestEncryptors.first_gcm.encrypt_and_sign("encrypted cookie") head :ok end def write_raw_cookie_two - cookies[:encrypted_cookie] = "#{encryptor_two.encrypt_and_sign("encrypted cookie")}" + cookies[:encrypted_cookie] = TestEncryptors.second_gcm.encrypt_and_sign("encrypted cookie") head :ok end @@ -162,15 +153,22 @@ module ApplicationTests RUBY add_to_config <<-RUBY + first_secret = Rails.application.key_generator.generate_key("first", 32) + second_secret = Rails.application.key_generator.generate_key("second", 32) + + ::TestEncryptors = Class.new do + class_attribute :first_gcm, default: ActiveSupport::MessageEncryptor.new(first_secret, cipher: "aes-256-gcm") + class_attribute :second_gcm, default: ActiveSupport::MessageEncryptor.new(second_secret, cipher: "aes-256-gcm") + end + config.action_dispatch.use_authenticated_cookie_encryption = true config.action_dispatch.encrypted_cookie_cipher = "aes-256-gcm" config.action_dispatch.authenticated_encrypted_cookie_salt = "salt" - config.action_dispatch.cookies_rotations.rotate :encrypted, - cipher: "aes-256-gcm", secret: "legacy secret one", salt: "salt one" - - config.action_dispatch.cookies_rotations.rotate :encrypted, - cipher: "aes-256-gcm", secret: "legacy secret two", salt: "salt two" + config.action_dispatch.cookies_rotations.tap do |cookies| + cookies.rotate :encrypted, first_secret + cookies.rotate :encrypted, second_secret + end RUBY require "#{app_path}/config/environment" diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index 36d1bf5bf2..a17988235a 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -301,8 +301,6 @@ module ApplicationTests end test "session upgrading from AES-CBC-HMAC encryption to AES-GCM encryption" do - skip "@kaspth will fix this" - app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do get ':controller(/:action)' -- cgit v1.2.3 From e65f2c4f9d25e47a0adc59a43f0a00c5397aedd8 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Tue, 26 Sep 2017 04:03:09 +0900 Subject: Add an space to RefineryCMS in Engine guide [ci skip] * According to the official website, it seems that "RefineryCMS" is "Refinery CMS". * See at: http://www.refinerycms.com/ --- guides/source/engines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/engines.md b/guides/source/engines.md index 343c224a7c..188620a683 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -63,7 +63,7 @@ authentication for its parent applications, or [Thredded](https://github.com/thredded/thredded), an engine that provides forum functionality. There's also [Spree](https://github.com/spree/spree) which provides an e-commerce platform, and -[RefineryCMS](https://github.com/refinery/refinerycms), a CMS engine. +[Refinery CMS](https://github.com/refinery/refinerycms), a CMS engine. Finally, engines would not have been possible without the work of James Adam, Piotr Sarnacki, the Rails Core Team, and a number of other people. If you ever -- cgit v1.2.3 From f60bf5af9faf6f8e24a5f95331f38f4dbec7aed9 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Mon, 25 Sep 2017 21:46:41 +0200 Subject: [ci skip] Don't mention unrotatable secret_key_base. --- guides/source/security.md | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/guides/source/security.md b/guides/source/security.md index 9e1dc518d2..a07d583f15 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -152,37 +152,39 @@ In test and development applications get a `secret_key_base` derived from the ap If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. -### Rotating Keys for Encrypted and Signed Cookies +### Rotating Encrypted and Signed Cookies Configurations -It is possible to rotate the `secret_key_base` as well as the salts, -ciphers, and digests used for both encrypted and signed cookies. Rotating -the `secret_key_base` is necessary if the value was exposed or leaked. -It is also useful to rotate this value for other more benign reasons, -such as an employee leaving your organization or changing hosting -environments. +Rotation is ideal for changing cookie configurations and ensuring old cookies +aren't immediately invalid. Your users then have a chance to visit your site, +get their cookie read with an old configuration and have it rewritten with the +new change. The rotation can then be removed once you're comfortable enough +users have had their chance to get their cookies upgraded. -For example to rotate out an old `secret_key_base`, we can define signed and -encrypted rotations as follows: +It's possible to rotate the ciphers and digests used for encrypted and signed cookies. + +For instance to change the digest used for signed cookies from SHA1 to SHA256, +you would first assign the new configuration value: ```ruby -Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| - cookies.rotate :encrypted, secret: Rails.application.credentials.old_secret_key_base - cookies.rotate :signed, secret: Rails.application.credentials.old_secret_key_base -end +Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256" ``` -It's also possible to set up multiple rotations. For instance to use `SHA512` -for signed cookies while rotating out SHA256 and SHA1 digests, we'd do: +Then you'd set up a rotation with the old configuration to keep it alive. ```ruby -Rails.application.config.action_dispatch.signed_cookie_digest = "SHA512" - Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| cookies.rotate :signed, digest: "SHA256" - cookies.rotate :signed, digest: "SHA1" end ``` +Then any written signed cookies will be digested with SHA256. Old cookies +that were written with SHA1 can still be read, and if accessed will be written +with the new digest so they're upgraded and won't be invalid when you remove the +rotation. + +Once users with SHA1 digested signed cookies should no longer have a chance to +have their cookies rewritten, remove the rotation. + While you can setup as many rotations as you'd like it's not common to have many rotations going at any one time. -- cgit v1.2.3 From b9665c800a18e3503a6d7a7902ec41efa65dfeb3 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Tue, 26 Sep 2017 07:45:42 +0900 Subject: Remove unused fixtures `about_yml_plugins` is no longer used since 82b9b15 --- railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml | 1 - railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb | 3 --- .../test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb | 3 --- 3 files changed, 7 deletions(-) delete mode 100644 railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml delete mode 100644 railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb delete mode 100644 railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml b/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml deleted file mode 100644 index fe80872a16..0000000000 --- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml +++ /dev/null @@ -1 +0,0 @@ -# an empty YAML file - any content in here seems to get parsed as a string \ No newline at end of file diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb deleted file mode 100644 index 1a82a2bdd4..0000000000 --- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -# intentionally empty diff --git a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb deleted file mode 100644 index 1a82a2bdd4..0000000000 --- a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -# intentionally empty -- cgit v1.2.3 From e03c9066ff83cac409281d6fc5e2377493009364 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Fri, 8 Sep 2017 22:30:39 +0800 Subject: PERF: Partially recover some performance when preloading. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Benchmark Script: ``` require 'active_record' require 'benchmark/ips' ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL')) ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name, :email t.integer :topic_id t.timestamps null: false end create_table :topics, force: true do |t| t.string :title t.timestamps null: false end end attributes = { name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', email: 'foobar@email.com' } class Topic < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :topic end 100.times do User.create!(attributes) end users = User.first(50) Topic.create!(title: 'This is a topic', users: users) Benchmark.ips do |x| x.config(time: 10, warmup: 5) x.report("preload") do User.includes(:topic).all.to_a end end ``` Before: ``` Calculating ------------------------------------- preload 40.000 i/100ms ------------------------------------------------- preload 407.962 (± 1.5%) i/s - 4.080k ``` After: ``` alculating ------------------------------------- preload 43.000 i/100ms ------------------------------------------------- preload 427.567 (± 1.6%) i/s - 4.300k ``` --- .../lib/active_record/associations/preloader/association.rb | 12 +++--------- .../lib/active_record/attribute_methods/primary_key.rb | 6 ++++-- activerecord/lib/active_record/attribute_methods/read.rb | 5 +++-- activerecord/lib/active_record/attribute_methods/write.rb | 5 +++-- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index fe696e0d6e..607d376a08 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -50,20 +50,14 @@ module ActiveRecord end def owner_keys - unless defined?(@owner_keys) - @owner_keys = owners.map do |owner| - owner[owner_key_name] - end - @owner_keys.uniq! - @owner_keys.compact! - end - @owner_keys + @owner_keys ||= owners_by_key.keys end def owners_by_key unless defined?(@owners_by_key) @owners_by_key = owners.each_with_object({}) do |owner, h| - h[convert_key(owner[owner_key_name])] = owner + key = convert_key(owner[owner_key_name]) + h[key] = owner if key end end @owners_by_key diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 63c059e291..d8fc046e10 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -17,13 +17,15 @@ module ActiveRecord # Returns the primary key value. def id sync_with_transaction_state - _read_attribute(self.class.primary_key) if self.class.primary_key + primary_key = self.class.primary_key + _read_attribute(primary_key) if primary_key end # Sets the primary key value. def id=(value) sync_with_transaction_state - _write_attribute(self.class.primary_key, value) if self.class.primary_key + primary_key = self.class.primary_key + _write_attribute(primary_key, value) if primary_key end # Queries the primary key value. diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index b070235684..4077250583 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -58,8 +58,9 @@ module ActiveRecord attr_name.to_s end - name = self.class.primary_key if name == "id".freeze && self.class.primary_key - sync_with_transaction_state if name == self.class.primary_key + primary_key = self.class.primary_key + name = primary_key if name == "id".freeze && primary_key + sync_with_transaction_state if name == primary_key _read_attribute(name, &block) end diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index 37891ce2ef..bb0ec6a8c3 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -39,8 +39,9 @@ module ActiveRecord attr_name.to_s end - name = self.class.primary_key if name == "id".freeze && self.class.primary_key - sync_with_transaction_state if name == self.class.primary_key + primary_key = self.class.primary_key + name = primary_key if name == "id".freeze && primary_key + sync_with_transaction_state if name == primary_key _write_attribute(name, value) end -- cgit v1.2.3 From 5755f57f3e887df93e11c4b3efb8bf21226744cc Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Tue, 26 Sep 2017 14:54:22 +0900 Subject: Fix indentation in CHANGELOG [ci skip] --- actionpack/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 1d4b27a0f9..16090e7946 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -7,14 +7,14 @@ *Michael J Coyne* -* Use Capybara registered `:puma` server config. +* Use Capybara registered `:puma` server config. The Capybara registered `:puma` server ensures the puma server is run in process so connection sharing and open request detection work correctly by default. *Thomas Walpole* -* Cookies `:expires` option supports `ActiveSupport::Duration` object. +* Cookies `:expires` option supports `ActiveSupport::Duration` object. cookies[:user_name] = { value: "assain", expires: 1.hour } cookies[:key] = { value: "a yummy cookie", expires: 6.months } @@ -23,7 +23,7 @@ *Assain Jaleel* -* Enforce signed/encrypted cookie expiry server side. +* Enforce signed/encrypted cookie expiry server side. Rails can thwart attacks by malicious clients that don't honor a cookie's expiry. -- cgit v1.2.3 From cadf6f85f3a44a3c7782e03b266af94c9fed42c0 Mon Sep 17 00:00:00 2001 From: yalab Date: Tue, 26 Sep 2017 17:56:38 +0900 Subject: Fixed broken `bundle exec rake install` --- activestorage/Rakefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activestorage/Rakefile b/activestorage/Rakefile index aa71a65f6e..2aa4d2a76f 100644 --- a/activestorage/Rakefile +++ b/activestorage/Rakefile @@ -11,4 +11,6 @@ Rake::TestTask.new do |test| test.warning = false end +task :package + task default: :test -- cgit v1.2.3 From 4b629eca21fbecf9a49f00f3933ad4c28ad78600 Mon Sep 17 00:00:00 2001 From: Richard Machielse Date: Mon, 25 Sep 2017 13:03:52 +0200 Subject: ActionCable: use find method when unsubscribing If a frontend for some reason tries to unsubscribe from a non existing subscription, the following error is logged: Could not execute command from ({"command"=>"unsubscribe", "identifier"=>"{\"channel\":\"SomeChannel\"}"}) [NoMethodError - undefined method `unsubscribe_from_channel' for nil:NilClass] Instead, it will now properly log: Could not execute command from ({"command"=>"unsubscribe", "identifier"=>"{\"channel\":\"SomeChannel\"}"}) [RuntimeError - Unable to find subscription with identifier: {"channel":"SomeChannel"}] --- actioncable/lib/action_cable/connection/subscriptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actioncable/lib/action_cable/connection/subscriptions.rb b/actioncable/lib/action_cable/connection/subscriptions.rb index faafd6d0a6..bb8d64e27a 100644 --- a/actioncable/lib/action_cable/connection/subscriptions.rb +++ b/actioncable/lib/action_cable/connection/subscriptions.rb @@ -43,7 +43,7 @@ module ActionCable def remove(data) logger.info "Unsubscribing from channel: #{data['identifier']}" - remove_subscription subscriptions[data["identifier"]] + remove_subscription find(data) end def remove_subscription(subscription) -- cgit v1.2.3 From 8f24923d7ad3c838437fba2ee3e5eee655660bbc Mon Sep 17 00:00:00 2001 From: hupfis Date: Tue, 26 Sep 2017 13:28:46 +0200 Subject: Fix typo in 'find_in_batches' example --- guides/source/active_record_querying.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 678b80516f..3573c3c77b 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -414,7 +414,7 @@ end `find_in_batches` works on model classes, as seen above, and also on relations: ```ruby -Invoice.pending.find_in_batches do |invoice| +Invoice.pending.find_in_batches do |invoices| pending_invoices_export.add_invoices(invoices) end ``` -- cgit v1.2.3 From 9cf7e3494f5bd34f1382c1ff4ea3d811a4972ae2 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 26 Sep 2017 13:22:12 -0600 Subject: Treat `Set` as an `Array` in `Relation#where` I do not want to set the expectation that any enumerable object should behave this way, but this case in particular comes up frequently enough that I'm caving on this one. Fixes #30684. --- activerecord/CHANGELOG.md | 5 +++++ .../lib/active_record/relation/predicate_builder.rb | 1 + activerecord/test/cases/relations_test.rb | 13 +++++++++++++ 3 files changed, 19 insertions(+) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c34236d4be..7cae105ddb 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Passing a `Set` to `Relation#where` now behaves the same as passing an + array. + + *Sean Griffin* + * Use given algorithm while removing index from database. Fixes #24190. diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 5c42414072..be4b169f67 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -13,6 +13,7 @@ module ActiveRecord register_handler(Range, RangeHandler.new(self)) register_handler(Relation, RelationHandler.new) register_handler(Array, ArrayHandler.new(self)) + register_handler(Set, ArrayHandler.new(self)) end def build_from_hash(attributes) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ae1dc35bff..6270533dc2 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1912,6 +1912,19 @@ class RelationTest < ActiveRecord::TestCase end end + test "#where with set" do + david = authors(:david) + mary = authors(:mary) + + authors = Author.where(name: ["David", "Mary"].to_set) + assert_equal [david, mary], authors + end + + test "#where with empty set" do + authors = Author.where(name: Set.new) + assert_empty authors + end + private def custom_post_relation table_alias = Post.arel_table.alias("omg_posts") -- cgit v1.2.3 From 3e0a858aedb9cb3698843f8b1de8a80876f75aa7 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 27 Sep 2017 00:02:11 +0300 Subject: Change :github git source for bug report templates :github source uses `git://` url by default, `https://` is recommended. See http://bundler.io/v1.15/guides/git.html#security We do the same in our `Gemfile` and templates. --- guides/bug_report_templates/action_controller_gem.rb | 1 + guides/bug_report_templates/action_controller_master.rb | 1 + guides/bug_report_templates/active_job_gem.rb | 1 + guides/bug_report_templates/active_job_master.rb | 1 + guides/bug_report_templates/active_record_gem.rb | 1 + guides/bug_report_templates/active_record_master.rb | 1 + guides/bug_report_templates/active_record_migrations_gem.rb | 1 + guides/bug_report_templates/active_record_migrations_master.rb | 1 + guides/bug_report_templates/benchmark.rb | 1 + guides/bug_report_templates/generic_gem.rb | 1 + guides/bug_report_templates/generic_master.rb | 1 + 11 files changed, 11 insertions(+) diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb index 4d8d8db3e5..fc4effac30 100644 --- a/guides/bug_report_templates/action_controller_gem.rb +++ b/guides/bug_report_templates/action_controller_gem.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. gem "rails", "5.1.0" end diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb index 1f862e07da..dc99573ca1 100644 --- a/guides/bug_report_templates/action_controller_master.rb +++ b/guides/bug_report_templates/action_controller_master.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" end diff --git a/guides/bug_report_templates/active_job_gem.rb b/guides/bug_report_templates/active_job_gem.rb index af777a86ef..3cedafd482 100644 --- a/guides/bug_report_templates/active_job_gem.rb +++ b/guides/bug_report_templates/active_job_gem.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. gem "activejob", "5.1.0" end diff --git a/guides/bug_report_templates/active_job_master.rb b/guides/bug_report_templates/active_job_master.rb index 39fb3f60a6..998632676a 100644 --- a/guides/bug_report_templates/active_job_master.rb +++ b/guides/bug_report_templates/active_job_master.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" end diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb index 168e2dcc66..55c2ab8a74 100644 --- a/guides/bug_report_templates/active_record_gem.rb +++ b/guides/bug_report_templates/active_record_gem.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. gem "activerecord", "5.1.0" gem "sqlite3" diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb index cbd2cff2b8..659c0a1d5e 100644 --- a/guides/bug_report_templates/active_record_master.rb +++ b/guides/bug_report_templates/active_record_master.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" gem "sqlite3" diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb index b931ed0beb..e5a336768b 100644 --- a/guides/bug_report_templates/active_record_migrations_gem.rb +++ b/guides/bug_report_templates/active_record_migrations_gem.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. gem "activerecord", "5.1.0" gem "sqlite3" diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb index 2c009c0563..251bc52c4e 100644 --- a/guides/bug_report_templates/active_record_migrations_master.rb +++ b/guides/bug_report_templates/active_record_migrations_master.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" gem "sqlite3" diff --git a/guides/bug_report_templates/benchmark.rb b/guides/bug_report_templates/benchmark.rb index d0f5a634bc..5c8c4cc3c6 100644 --- a/guides/bug_report_templates/benchmark.rb +++ b/guides/bug_report_templates/benchmark.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" gem "benchmark-ips" diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb index c990bda005..2cff854621 100644 --- a/guides/bug_report_templates/generic_gem.rb +++ b/guides/bug_report_templates/generic_gem.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. gem "activesupport", "5.1.0" end diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb index 1a9b99b624..12334a0106 100644 --- a/guides/bug_report_templates/generic_master.rb +++ b/guides/bug_report_templates/generic_master.rb @@ -9,6 +9,7 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" end -- cgit v1.2.3 From 8d3bf8b66c783419600ad815db8a5405fe573b0b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 27 Sep 2017 06:18:23 +0900 Subject: Don't generate `foreign_type` if `options[:polymorphic]` is not given Because the reflection doesn't have `foreign_type` unless the association is a polymorphic association. --- activerecord/lib/active_record/reflection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 032df925bf..97adfb4352 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -426,7 +426,7 @@ module ActiveRecord def initialize(name, scope, options, active_record) super @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type") - @foreign_type = options[:foreign_type] || "#{name}_type" + @foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type") @constructable = calculate_constructable(macro, options) @association_scope_cache = Concurrent::Map.new -- cgit v1.2.3 From 25d3e7334beda3114a1c5ce01f0c52cbb15f3b18 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 27 Sep 2017 07:19:16 +0900 Subject: Remove unused `cached_columns` and `time_related_columns_on_topic` in `AttributeMethodsTest` These are no longer used since 66736c8e. --- activerecord/test/cases/attribute_methods_test.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 0ea8ef5cea..2d67c57cfb 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -1019,14 +1019,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase ActiveRecord::Base.time_zone_aware_types = old_types end - def cached_columns - Topic.columns.map(&:name) - end - - def time_related_columns_on_topic - Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) } - end - def privatize(method_signature) @target.class_eval(<<-private_method, __FILE__, __LINE__ + 1) private -- cgit v1.2.3 From 0e3b4d64c0a61793204873d844a546a88b10940b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 27 Sep 2017 07:38:45 +0900 Subject: Remove unused code that was copied from actionpack `abstract_unit.rb` in actionview was copied from actionpack in the commit eb23754e. But some part is never used for actionview's tests. --- actionview/test/abstract_unit.rb | 53 +--------------------------------------- 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index c98270bd12..1b64799f85 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -26,14 +26,6 @@ require "active_record" require "pp" # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late -module Rails - class << self - def env - @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test") - end - end -end - ActiveSupport::Dependencies.hook! Thread.abort_on_exception = true @@ -110,12 +102,6 @@ module ActionDispatch end end -module ActiveSupport - class TestCase - include ActionDispatch::DrawOnce - end -end - class RoutedRackApp attr_reader :routes @@ -162,29 +148,6 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase self.app = build_app - # 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 - private - def controller_reference(controller_param) - controller_param - end - - def dispatch(controller, action, env) - [200, { "Content-Type" => "text/html" }, ["#{controller}##{action}"]] - end - end - - def self.stub_controllers - old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher - ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher } - ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher } - yield ActionDispatch::Routing::RouteSet.new - ensure - ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher } - ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher } - end - def with_routing(&block) temporary_routes = ActionDispatch::Routing::RouteSet.new old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes) @@ -196,21 +159,6 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase self.class.app = old_app silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) } end - - def with_autoload_path(path) - path = File.join(File.expand_path("fixtures", __dir__), path) - if ActiveSupport::Dependencies.autoload_paths.include?(path) - yield - else - begin - ActiveSupport::Dependencies.autoload_paths << path - yield - ensure - ActiveSupport::Dependencies.autoload_paths.reject! { |p| p == path } - ActiveSupport::Dependencies.clear - end - end - end end ActionView::RoutingUrlFor.include(ActionDispatch::Routing::UrlFor) @@ -233,6 +181,7 @@ module ActionController class TestCase include ActionDispatch::TestProcess include ActionDispatch::SharedRoutes + include ActionDispatch::DrawOnce end end -- cgit v1.2.3 From 40a63e52d275a94227761f79b4a2544aaad8210a Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 27 Sep 2017 08:51:37 +0900 Subject: Include `ActionDispatch::DrawOnce` in `ActiveSupport::TestCase` It is also used in `BlockTestCase`. --- actionview/test/abstract_unit.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index 1b64799f85..f20a66c2d2 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -181,7 +181,6 @@ module ActionController class TestCase include ActionDispatch::TestProcess include ActionDispatch::SharedRoutes - include ActionDispatch::DrawOnce end end @@ -223,6 +222,7 @@ module ActionDispatch end class ActiveSupport::TestCase + include ActionDispatch::DrawOnce include ActiveSupport::Testing::MethodCallAssertions # Skips the current run on Rubinius using Minitest::Assertions#skip -- cgit v1.2.3 From 51b6c3429f41ebbc571b8fcbe7c21d3131b832d8 Mon Sep 17 00:00:00 2001 From: Thomas Cannon Date: Tue, 26 Sep 2017 10:37:32 -0400 Subject: `Postgres::OID::Range` serializes to a `Range`, quote in `Quoting` PostgreSQL 9.1+ introduced range types, and Rails added support for using this datatype in ActiveRecord. However, the serialization of `PostgreSQL::OID::Range` was incomplete, because it did not properly quote the bounds that make up the range. A clear example of this is a `tsrange`. Normally, ActiveRecord quotes Date/Time objects to include the milliseconds. However, the way `PostgreSQL::OID::Range` serialized its bounds, the milliseconds were dropped. This meant that the value was incomplete and not equal to the submitted value. An example of normal timestamps vs. a `tsrange`. Note how the bounds for the range do not include their milliseconds (they were present in the ruby Range): UPDATE "iterations" SET "updated_at" = $1, "range" = $2 WHERE "iterations"."id" = $3 [["updated_at", "2017-09-23 17:07:01.304864"], ["range", "[2017-09-23 00:00:00 UTC,2017-09-23 23:59:59 UTC]"], ["id", 1234]] `PostgreSQL::OID::Range` serialized the range by interpolating a string for the range, which works for most cases, but does not work for timestamps: def serialize(value) if value.is_a?(::Range) from = type_cast_single_for_database(value.begin) to = type_cast_single_for_database(value.end) "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}" else super end end (byebug) from = type_cast_single_for_database(value.begin) 2010-01-01 13:30:00 UTC (byebug) to = type_cast_single_for_database(value.end) 2011-02-02 19:30:00 UTC (byebug) "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}" "[2010-01-01 13:30:00 UTC,2011-02-02 19:30:00 UTC)" @sgrif (the original implementer for Postgres Range support) provided some feedback about where the quoting should occur: Yeah, quoting at all is definitely wrong here. I'm not sure what I was thinking in 02579b5, but what this is doing is definitely in the wrong place. It should probably just be returning a range of subtype.serialize(value.begin) and subtype.serialize(value.end), and letting the adapter handle the rest. `Postgres::OID::Range` now returns a `Range` object, and `ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting` can now encode and quote a `Range`: def encode_range(range) "[#{type_cast(range.first)},#{type_cast(range.last)}#{range.exclude_end? ? ')' : ']'}" end ... encode_range(range) #=> "['2010-01-01 13:30:00.670277','2011-02-02 19:30:00.745125')" This commit includes tests to make sure the milliseconds are preserved in `tsrange` and `tstzrange` columns --- activerecord/CHANGELOG.md | 20 +++++++++ .../connection_adapters/postgresql/oid/range.rb | 2 +- .../connection_adapters/postgresql/quoting.rb | 8 ++++ .../test/cases/adapters/postgresql/range_test.rb | 51 ++++++++++++++++++++++ .../cases/adapters/postgresql/type_lookup_test.rb | 2 +- 5 files changed, 81 insertions(+), 2 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7cae105ddb..f73e27b91f 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,23 @@ +* PostgreSQL `tsrange` now preserves subsecond precision + + PostgreSQL 9.1+ introduced range types, and Rails added support for using + this datatype in ActiveRecord. However, the serialization of + PostgreSQL::OID::Range was incomplete, because it did not properly + cast the bounds that make up the range. This led to subseconds being + dropped in SQL commands: + + (byebug) from = type_cast_single_for_database(range.first) + 2010-01-01 13:30:00 UTC + + (byebug) to = type_cast_single_for_database(range.last) + 2011-02-02 19:30:00 UTC + + (byebug) "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}" + "[2010-01-01 13:30:00 UTC,2011-02-02 19:30:00 UTC)" + + (byebug) "[#{type_cast(from)},#{type_cast(to)}#{value.exclude_end? ? ')' : ']'}" + "['2010-01-01 13:30:00.670277','2011-02-02 19:30:00.745125')" + * Passing a `Set` to `Relation#where` now behaves the same as passing an array. 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 7d5d7d91e6..a89aa5ea09 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb @@ -35,7 +35,7 @@ module ActiveRecord if value.is_a?(::Range) from = type_cast_single_for_database(value.begin) to = type_cast_single_for_database(value.end) - "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}" + ::Range.new(from, to, value.exclude_end?) else super end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index a0a22ba0f1..9fdeab06c1 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -101,6 +101,8 @@ module ActiveRecord end when OID::Array::Data _quote(encode_array(value)) + when Range + _quote(encode_range(value)) else super end @@ -117,6 +119,8 @@ module ActiveRecord value.to_s when OID::Array::Data encode_array(value) + when Range + encode_range(value) else super end @@ -133,6 +137,10 @@ module ActiveRecord result end + def encode_range(range) + "[#{type_cast(range.first)},#{type_cast(range.last)}#{range.exclude_end? ? ')' : ']'}" + end + def determine_encoding_of_strings_in_array(value) case value when ::Array then determine_encoding_of_strings_in_array(value.first) diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index b4a776d04d..a75fdef698 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -232,6 +232,57 @@ _SQL end end + def test_create_tstzrange_preserve_usec + tstzrange = Time.parse("2010-01-01 14:30:00.670277 +0100")...Time.parse("2011-02-02 14:30:00.745125 CDT") + round_trip(@new_range, :tstz_range, tstzrange) + assert_equal @new_range.tstz_range, tstzrange + assert_equal @new_range.tstz_range, Time.parse("2010-01-01 13:30:00.670277 UTC")...Time.parse("2011-02-02 19:30:00.745125 UTC") + end + + def test_update_tstzrange_preserve_usec + assert_equal_round_trip(@first_range, :tstz_range, + Time.parse("2010-01-01 14:30:00.245124 CDT")...Time.parse("2011-02-02 14:30:00.451274 CET")) + assert_nil_round_trip(@first_range, :tstz_range, + Time.parse("2010-01-01 14:30:00.245124 +0100")...Time.parse("2010-01-01 13:30:00.245124 +0000")) + end + + def test_create_tsrange_preseve_usec + tz = ::ActiveRecord::Base.default_timezone + assert_equal_round_trip(@new_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0, 125435)...Time.send(tz, 2011, 2, 2, 14, 30, 0, 225435)) + end + + def test_update_tsrange_preserve_usec + tz = ::ActiveRecord::Base.default_timezone + assert_equal_round_trip(@first_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)...Time.send(tz, 2011, 2, 2, 14, 30, 0, 224242)) + assert_nil_round_trip(@first_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)...Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)) + end + + def test_timezone_awareness_tsrange_preserve_usec + tz = "Pacific Time (US & Canada)" + + in_time_zone tz do + PostgresqlRange.reset_column_information + time_string = "2017-09-26 07:30:59.132451 -0700" + time = Time.zone.parse(time_string) + assert time.usec > 0 + + record = PostgresqlRange.new(ts_range: time_string..time_string) + assert_equal time..time, record.ts_range + assert_equal ActiveSupport::TimeZone[tz], record.ts_range.begin.time_zone + assert_equal time.usec, record.ts_range.begin.usec + + record.save! + record.reload + + assert_equal time..time, record.ts_range + assert_equal ActiveSupport::TimeZone[tz], record.ts_range.begin.time_zone + assert_equal time.usec, record.ts_range.begin.usec + end + end + def test_create_numrange assert_equal_round_trip(@new_range, :num_range, BigDecimal.new("0.5")...BigDecimal.new("1")) diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb index 449023b6eb..8212ed4263 100644 --- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb +++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb @@ -30,6 +30,6 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase big_range = 0..123456789123456789 assert_raises(ActiveModel::RangeError) { int_range.serialize(big_range) } - assert_equal "[0,123456789123456789]", bigint_range.serialize(big_range) + assert_equal "[0,123456789123456789]", @connection.type_cast(bigint_range.serialize(big_range)) end end -- cgit v1.2.3 From 801ccab22a8efc2611300a14a8b373a43aa0de28 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 27 Sep 2017 13:25:14 +0900 Subject: Add test case for `arel_attribute` with a custom table Since #29301, `arel_attribute` respects a custom table name. --- activerecord/test/cases/relations_test.rb | 4 ++++ activerecord/test/models/post.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 6270533dc2..4edaf79e9a 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1806,6 +1806,10 @@ class RelationTest < ActiveRecord::TestCase assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).take end + test "arel_attribute respects a custom table" do + assert_equal [posts(:welcome)], custom_post_relation.ranked_by_comments.limit_by(1).to_a + end + test "#load" do relation = Post.all assert_queries(1) do diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 4c8e847354..935a11e811 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -21,7 +21,7 @@ class Post < ActiveRecord::Base scope :containing_the_letter_a, -> { where("body LIKE '%a%'") } scope :titled_with_an_apostrophe, -> { where("title LIKE '%''%'") } - scope :ranked_by_comments, -> { order("comments_count DESC") } + scope :ranked_by_comments, -> { order(arel_attribute(:comments_count).desc) } scope :limit_by, lambda { |l| limit(l) } scope :locked, -> { lock } -- cgit v1.2.3 From de9e3238a008f8f20c5beedefb8787b6e51359d0 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 27 Sep 2017 13:18:34 +0900 Subject: Ensure `HashWithIndifferentAccess#transform_keys` to return `HashWithIndifferentAccess` Currently, `#transform_values`, `#select` and `#reject` return instance of `HashWithIndifferentAccess`. But `#transform_keys` returns instance of Hash. This behavior is a bit confusing. I think that `HashWithIndifferentAccess#transform_keys` should also return instance of `HashWithIndifferentAccess` as well as other methods. --- activesupport/CHANGELOG.md | 4 ++++ activesupport/lib/active_support/hash_with_indifferent_access.rb | 5 +++++ activesupport/test/hash_with_indifferent_access_test.rb | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 487984cbd3..493ebeb01f 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,7 @@ +* Return an instance of `HashWithIndifferentAccess` from `HashWithIndifferentAccess#transform_keys`. + + *Yuji Yaginuma* + * Add key rotation support to `MessageEncryptor` and `MessageVerifier` This change introduces a `rotate` method to both the `MessageEncryptor` and diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 12291af443..fcc13feb8c 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -306,6 +306,11 @@ module ActiveSupport dup.tap { |hash| hash.transform_values!(*args, &block) } end + def transform_keys(*args, &block) + return to_enum(:transform_keys) unless block_given? + dup.tap { |hash| hash.transform_keys!(*args, &block) } + end + def compact dup.tap(&:compact!) end diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb index b3788ee65c..b878ac20fa 100644 --- a/activesupport/test/hash_with_indifferent_access_test.rb +++ b/activesupport/test/hash_with_indifferent_access_test.rb @@ -399,6 +399,13 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings end + def test_indifferent_transform_keys + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).transform_keys { |k| k * 2 } + + assert_equal({ "aa" => 1, "bb" => 2 }, hash) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + def test_indifferent_compact hash_contain_nil_value = @strings.merge("z" => nil) hash = ActiveSupport::HashWithIndifferentAccess.new(hash_contain_nil_value) -- cgit v1.2.3 From cf56397ccd10174d94f60331e4a55ff765b3485b Mon Sep 17 00:00:00 2001 From: yalab Date: Wed, 20 Sep 2017 14:04:12 +0900 Subject: scaffold nested name controller should be fine. --- .../erb/scaffold/templates/_form.html.erb | 2 +- .../erb/scaffold/templates/index.html.erb | 8 ++--- railties/lib/rails/generators/named_base.rb | 35 ++++++++++++++++++++-- .../scaffold_controller/templates/controller.rb | 4 +-- railties/test/generators/named_base_test.rb | 13 ++++++++ .../scaffold_controller_generator_test.rb | 23 ++++++++++++++ 6 files changed, 75 insertions(+), 10 deletions(-) diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb index 4f2e84f924..0eb9d82bbb 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb @@ -1,4 +1,4 @@ -<%%= form_with(model: <%= singular_table_name %>, local: true) do |form| %> +<%%= form_with(model: <%= model_resource_name %>, local: true) do |form| %> <%% if <%= singular_table_name %>.errors.any? %>

<%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:

diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb index 5f4904fee1..e1ede7c713 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb @@ -18,9 +18,9 @@ <% attributes.reject(&:password_digest?).each do |attribute| -%> <%%= <%= singular_table_name %>.<%= attribute.name %> %> <% end -%> - <%%= link_to 'Show', <%= singular_table_name %> %> - <%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %> - <%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %> + <%%= link_to 'Show', <%= model_resource_name %> %> + <%%= link_to 'Edit', edit_<%= singular_route_name %>_path(<%= singular_table_name %>) %> + <%%= link_to 'Destroy', <%= model_resource_name %>, method: :delete, data: { confirm: 'Are you sure?' } %> <%% end %> @@ -28,4 +28,4 @@
-<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_table_name %>_path %> +<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_route_name %>_path %> diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index fe8447be23..5f602f1d52 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -100,11 +100,11 @@ module Rails end def index_helper # :doc: - uncountable? ? "#{plural_table_name}_index" : plural_table_name + uncountable? ? "#{plural_route_name}_index" : plural_route_name end def show_helper # :doc: - "#{singular_table_name}_url(@#{singular_table_name})" + "#{singular_route_name}_url(@#{singular_table_name})" end def edit_helper # :doc: @@ -112,7 +112,7 @@ module Rails end def new_helper # :doc: - "new_#{singular_table_name}_url" + "new_#{singular_route_name}_url" end def field_id(attribute_name) @@ -152,6 +152,35 @@ module Rails end end + def redirect_resource_name # :doc: + model_resource_name(prefix: "@") + end + + def model_resource_name(prefix: "") # :doc: + resource_name = "#{prefix}#{singular_table_name}" + if controller_class_path.empty? + resource_name + else + "[#{controller_class_path.map { |name| ":" + name }.join(", ")}, #{resource_name}]" + end + end + + def singular_route_name # :doc: + if controller_class_path.empty? + singular_table_name + else + "#{controller_class_path.join('_')}_#{singular_table_name}" + end + end + + def plural_route_name # :doc: + if controller_class_path.empty? + plural_table_name + else + "#{controller_class_path.join('_')}_#{plural_table_name}" + end + end + def assign_names!(name) @class_path = name.include?("/") ? name.split("/") : name.split("::") @class_path.map!(&:underscore) diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb index 42b9e34274..05f1c2b2d3 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb @@ -29,7 +29,7 @@ class <%= controller_class_name %>Controller < ApplicationController @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> if @<%= orm_instance.save %> - redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> + redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully created.'" %> else render :new end @@ -38,7 +38,7 @@ class <%= controller_class_name %>Controller < ApplicationController # PATCH/PUT <%= route_url %>/1 def update if @<%= orm_instance.update("#{singular_table_name}_params") %> - redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> + redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> else render :edit end diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 67f05926e3..64e9909859 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -131,6 +131,19 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "admin/foos", :controller_file_path assert_name g, "foos", :controller_file_name assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_user", :singular_route_name + assert_name g, "admin_users", :plural_route_name + assert_name g, "[:admin, @user]", :redirect_resource_name + assert_name g, "[:admin, user]", :model_resource_name + assert_name g, "admin_users", :index_helper + end + + def test_scaffold_plural_names + g = generator ["User"] + assert_name g, "@user", :redirect_resource_name + assert_name g, "user", :model_resource_name + assert_name g, "user", :singular_route_name + assert_name g, "users", :plural_route_name end private diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 384524aba9..513b037043 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -174,6 +174,29 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase assert_instance_method :index, content do |m| assert_match("@users = User.all", m) end + + assert_instance_method :create, content do |m| + assert_match("redirect_to [:admin, @user]", m) + end + + assert_instance_method :update, content do |m| + assert_match("redirect_to [:admin, @user]", m) + end + end + + assert_file "app/views/admin/users/index.html.erb" do |content| + assert_match("'Show', [:admin, user]", content) + assert_match("'Edit', edit_admin_user_path(user)", content) + assert_match("'Destroy', [:admin, user]", content) + assert_match("'New User', new_admin_user_path", content) + end + + assert_file "app/views/admin/users/new.html.erb" do |content| + assert_match("'Back', admin_users_path", content) + end + + assert_file "app/views/admin/users/_form.html.erb" do |content| + assert_match("model: [:admin, user]", content) end end -- cgit v1.2.3 From a151d8ad89a3d353160d85945ab9ad44cb9e6f7a Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 27 Sep 2017 17:55:43 +0900 Subject: Add newlines before/after the `git_source` in bug report templates [ci skip] --- guides/bug_report_templates/action_controller_gem.rb | 2 ++ guides/bug_report_templates/action_controller_master.rb | 2 ++ guides/bug_report_templates/active_job_gem.rb | 2 ++ guides/bug_report_templates/active_job_master.rb | 2 ++ guides/bug_report_templates/active_record_gem.rb | 2 ++ guides/bug_report_templates/active_record_master.rb | 2 ++ guides/bug_report_templates/active_record_migrations_gem.rb | 2 ++ guides/bug_report_templates/active_record_migrations_master.rb | 2 ++ guides/bug_report_templates/benchmark.rb | 2 ++ guides/bug_report_templates/generic_gem.rb | 2 ++ guides/bug_report_templates/generic_master.rb | 2 ++ 11 files changed, 22 insertions(+) diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb index fc4effac30..341724cdcd 100644 --- a/guides/bug_report_templates/action_controller_gem.rb +++ b/guides/bug_report_templates/action_controller_gem.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + # Activate the gem you are reporting the issue against. gem "rails", "5.1.0" end diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb index dc99573ca1..558d9bf3e2 100644 --- a/guides/bug_report_templates/action_controller_master.rb +++ b/guides/bug_report_templates/action_controller_master.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" end diff --git a/guides/bug_report_templates/active_job_gem.rb b/guides/bug_report_templates/active_job_gem.rb index 3cedafd482..013d1f8602 100644 --- a/guides/bug_report_templates/active_job_gem.rb +++ b/guides/bug_report_templates/active_job_gem.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + # Activate the gem you are reporting the issue against. gem "activejob", "5.1.0" end diff --git a/guides/bug_report_templates/active_job_master.rb b/guides/bug_report_templates/active_job_master.rb index 998632676a..ce480cbb52 100644 --- a/guides/bug_report_templates/active_job_master.rb +++ b/guides/bug_report_templates/active_job_master.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" end diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb index 55c2ab8a74..921917fbe9 100644 --- a/guides/bug_report_templates/active_record_gem.rb +++ b/guides/bug_report_templates/active_record_gem.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + # Activate the gem you are reporting the issue against. gem "activerecord", "5.1.0" gem "sqlite3" diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb index 659c0a1d5e..78411e2d57 100644 --- a/guides/bug_report_templates/active_record_master.rb +++ b/guides/bug_report_templates/active_record_master.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" gem "sqlite3" diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb index e5a336768b..f75b6fd932 100644 --- a/guides/bug_report_templates/active_record_migrations_gem.rb +++ b/guides/bug_report_templates/active_record_migrations_gem.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + # Activate the gem you are reporting the issue against. gem "activerecord", "5.1.0" gem "sqlite3" diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb index 251bc52c4e..60416ed42f 100644 --- a/guides/bug_report_templates/active_record_migrations_master.rb +++ b/guides/bug_report_templates/active_record_migrations_master.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" gem "sqlite3" diff --git a/guides/bug_report_templates/benchmark.rb b/guides/bug_report_templates/benchmark.rb index 5c8c4cc3c6..fb51273e3e 100644 --- a/guides/bug_report_templates/benchmark.rb +++ b/guides/bug_report_templates/benchmark.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" gem "benchmark-ips" diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb index 2cff854621..60e8322c2a 100644 --- a/guides/bug_report_templates/generic_gem.rb +++ b/guides/bug_report_templates/generic_gem.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + # Activate the gem you are reporting the issue against. gem "activesupport", "5.1.0" end diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb index 12334a0106..384c8b1833 100644 --- a/guides/bug_report_templates/generic_master.rb +++ b/guides/bug_report_templates/generic_master.rb @@ -9,7 +9,9 @@ end gemfile(true) do source "https://rubygems.org" + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" end -- cgit v1.2.3 From 0fb6b2d572b50493e3d69cea17eb60d3c91a0dbd Mon Sep 17 00:00:00 2001 From: Michael Coyne Date: Sun, 24 Sep 2017 13:38:27 -0400 Subject: Fixes for use_authenticated_cookie_encryption Use CBC encryption is this configuration value is set to false --- actionpack/lib/action_dispatch/middleware/cookies.rb | 13 ++++++++++--- actionpack/test/dispatch/cookies_test.rb | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index baffe200bc..eb193fcbfb 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -599,9 +599,16 @@ module ActionDispatch def initialize(parent_jar) super - key_len = ActiveSupport::MessageEncryptor.key_len(encrypted_cookie_cipher) - secret = request.key_generator.generate_key(request.authenticated_encrypted_cookie_salt, key_len) - @encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: encrypted_cookie_cipher, serializer: SERIALIZER) + if request.use_authenticated_cookie_encryption + key_len = ActiveSupport::MessageEncryptor.key_len(encrypted_cookie_cipher) + secret = request.key_generator.generate_key(request.authenticated_encrypted_cookie_salt, key_len) + @encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: encrypted_cookie_cipher, serializer: SERIALIZER) + else + key_len = ActiveSupport::MessageEncryptor.key_len("aes-256-cbc") + secret = request.key_generator.generate_key(request.encrypted_cookie_salt, key_len) + sign_secret = request.key_generator.generate_key(request.encrypted_signed_cookie_salt) + @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", serializer: SERIALIZER) + end request.cookies_rotations.encrypted.each do |*secrets, **options| @encryptor.rotate(*secrets, serializer: SERIALIZER, **options) diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 70587fa2b0..fca3b24372 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -899,6 +899,24 @@ class CookiesTest < ActionController::TestCase assert_nil @response.cookies["foo"] end + def test_use_authenticated_cookie_encryption_uses_legacy_hmac_aes_cbc_encrypiton + @request.env["action_dispatch.use_authenticated_cookie_encryption"] = false + + key_generator = @request.env["action_dispatch.key_generator"] + encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] + encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"] + secret = key_generator.generate_key(encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len("aes-256-cbc")) + sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt) + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", digest: "SHA1", serializer: Marshal) + + get :set_encrypted_cookie + + cookies = @controller.send :cookies + assert_not_equal "bar", cookies[:foo] + assert_equal "bar", cookies.encrypted[:foo] + assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + def test_legacy_hmac_aes_cbc_encrypted_marshal_cookie_is_upgraded_to_authenticated_encrypted_cookie key_generator = @request.env["action_dispatch.key_generator"] encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] -- cgit v1.2.3 From 521266f913083c3029567ffe647e08e39af7f18a Mon Sep 17 00:00:00 2001 From: Neil Matatall Date: Wed, 27 Sep 2017 07:14:37 -1000 Subject: Encourage html-safe API in layouts/rendering guide While the code example was not unsafe, it encourages the use of confusingly unsafe APIs (specifically `html_safe`). We have a safe alternative and we should encourage people to use it under all circumstances. --- guides/source/layouts_and_rendering.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index fe2477f2ae..b9b327252f 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -232,14 +232,14 @@ You can send an HTML string back to the browser by using the `:html` option to `render`: ```ruby -render html: "Not Found".html_safe +render html: helpers.tag.strong('Not Found') ``` TIP: This is useful when you're rendering a small snippet of HTML code. However, you might want to consider moving it to a template file if the markup is complex. -NOTE: When using `html:` option, HTML entities will be escaped if the string is not marked as HTML safe by using `html_safe` method. +NOTE: When using `html:` option, HTML entities will be escaped if the string is not composed with `html_safe`-aware APIs. #### Rendering JSON -- cgit v1.2.3 From 41e2fabf6d569c17099e7c1e013f82073c4f63b8 Mon Sep 17 00:00:00 2001 From: "T.J. Schuck" Date: Wed, 27 Sep 2017 14:44:03 -0400 Subject: Fix formatting in ActionDispatch::SSL middleware docs Before: https://monosnap.com/file/J6xewF0tYpm6dC9nSTe82ddsHAOcM5.png After: https://monosnap.com/file/0tCYicLXNqRHAEMDb81u0aLb3gH9Wf.png [ci skip] --- actionpack/lib/action_dispatch/middleware/ssl.rb | 71 +++++++++++++----------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb index 45290b6ac3..9e1684f689 100644 --- a/actionpack/lib/action_dispatch/middleware/ssl.rb +++ b/actionpack/lib/action_dispatch/middleware/ssl.rb @@ -1,50 +1,55 @@ # frozen_string_literal: true module ActionDispatch - # This middleware is added to the stack when `config.force_ssl = true`, and is passed - # the options set in `config.ssl_options`. It does three jobs to enforce secure HTTP + # This middleware is added to the stack when config.force_ssl = true, and is passed + # the options set in +config.ssl_options+. It does three jobs to enforce secure HTTP # requests: # - # 1. TLS redirect: Permanently redirects http:// requests to https:// - # with the same URL host, path, etc. Enabled by default. Set `config.ssl_options` - # to modify the destination URL - # (e.g. `redirect: { host: "secure.widgets.com", port: 8080 }`), or set - # `redirect: false` to disable this feature. + # 1. TLS redirect: Permanently redirects +http://+ requests to +https://+ + # with the same URL host, path, etc. Enabled by default. Set +config.ssl_options+ + # to modify the destination URL + # (e.g. redirect: { host: "secure.widgets.com", port: 8080 }), or set + # redirect: false to disable this feature. # - # 2. Secure cookies: Sets the `secure` flag on cookies to tell browsers they - # mustn't be sent along with http:// requests. Enabled by default. Set - # `config.ssl_options` with `secure_cookies: false` to disable this feature. + # Requests can opt-out of redirection with +exclude+: # - # 3. HTTP Strict Transport Security (HSTS): Tells the browser to remember - # this site as TLS-only and automatically redirect non-TLS requests. - # Enabled by default. Configure `config.ssl_options` with `hsts: false` to disable. + # config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } } # - # Set `config.ssl_options` with `hsts: { … }` to configure HSTS: - # * `expires`: How long, in seconds, these settings will stick. The minimum - # required to qualify for browser preload lists is `18.weeks`. Defaults to - # `180.days` (recommended). - # * `subdomains`: Set to `true` to tell the browser to apply these settings - # to all subdomains. This protects your cookies from interception by a - # vulnerable site on a subdomain. Defaults to `true`. - # * `preload`: Advertise that this site may be included in browsers' - # preloaded HSTS lists. HSTS protects your site on every visit *except the - # first visit* since it hasn't seen your HSTS header yet. To close this - # gap, browser vendors include a baked-in list of HSTS-enabled sites. - # Go to https://hstspreload.appspot.com to submit your site for inclusion. - # Defaults to `false`. + # 2. Secure cookies: Sets the +secure+ flag on cookies to tell browsers they + # must not be sent along with +http://+ requests. Enabled by default. Set + # +config.ssl_options+ with secure_cookies: false to disable this feature. # - # To turn off HSTS, omitting the header is not enough. Browsers will remember the - # original HSTS directive until it expires. Instead, use the header to tell browsers to - # expire HSTS immediately. Setting `hsts: false` is a shortcut for - # `hsts: { expires: 0 }`. + # 3. HTTP Strict Transport Security (HSTS): Tells the browser to remember + # this site as TLS-only and automatically redirect non-TLS requests. + # Enabled by default. Configure +config.ssl_options+ with hsts: false to disable. # - # Requests can opt-out of redirection with `exclude`: + # Set +config.ssl_options+ with hsts: { ... } to configure HSTS: # - # config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } } + # * +expires+: How long, in seconds, these settings will stick. The minimum + # required to qualify for browser preload lists is 18 weeks. Defaults to + # 180 days (recommended). + # + # * +subdomains+: Set to +true+ to tell the browser to apply these settings + # to all subdomains. This protects your cookies from interception by a + # vulnerable site on a subdomain. Defaults to +true+. + # + # * +preload+: Advertise that this site may be included in browsers' + # preloaded HSTS lists. HSTS protects your site on every visit except the + # first visit since it hasn't seen your HSTS header yet. To close this + # gap, browser vendors include a baked-in list of HSTS-enabled sites. + # Go to https://hstspreload.org to submit your site for inclusion. + # Defaults to +false+. + # + # To turn off HSTS, omitting the header is not enough. Browsers will remember the + # original HSTS directive until it expires. Instead, use the header to tell browsers to + # expire HSTS immediately. Setting hsts: false is a shortcut for + # hsts: { expires: 0 }. class SSL + # :stopdoc: + # Default to 180 days, the low end for https://www.ssllabs.com/ssltest/ # and greater than the 18-week requirement for browser preload lists. - HSTS_EXPIRES_IN = 15552000 + HSTS_EXPIRES_IN = 180.days.to_i def self.default_hsts_options { expires: HSTS_EXPIRES_IN, subdomains: true, preload: false } -- cgit v1.2.3 From 7f3ba970b4892262169971ccafd09fd10e21f3df Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 27 Sep 2017 23:59:21 +0300 Subject: Set version in activestorage/package.json in proper format. `5.2.0.alpha` => `5.2.0-alpha` System versioning isn't compliant with npm. --- activestorage/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/package.json b/activestorage/package.json index b481295a8d..8e6dd1c57f 100644 --- a/activestorage/package.json +++ b/activestorage/package.json @@ -1,6 +1,6 @@ { "name": "activestorage", - "version": "5.2.0.alpha", + "version": "5.2.0-alpha", "description": "Attach cloud and local files in Rails applications", "main": "app/assets/javascripts/activestorage.js", "files": [ -- cgit v1.2.3 From db6847dcb6448ceeebe6cc37cc7d960dc72a0082 Mon Sep 17 00:00:00 2001 From: Mikkel Malmberg Date: Sun, 24 Sep 2017 19:55:32 +0200 Subject: Add assert_enqueued_email_with to ActionMailer::TestHelper --- actionmailer/CHANGELOG.md | 8 +++++ actionmailer/lib/action_mailer/test_helper.rb | 42 +++++++++++++++++++++++++++ actionmailer/test/test_helper_test.rb | 42 +++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 9993a11c9d..fe60f8a6a1 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,3 +1,11 @@ +* Add `assert_enqueued_email_with` test helper. + + assert_enqueued_email_with ContactMailer, :welcome do + ContactMailer.welcome.deliver_later + end + + *Mikkel Malmberg* + * Allow Action Mailer classes to configure their delivery job. class MyMailer < ApplicationMailer diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb index ac8b944743..8ee4d06915 100644 --- a/actionmailer/lib/action_mailer/test_helper.rb +++ b/actionmailer/lib/action_mailer/test_helper.rb @@ -93,6 +93,48 @@ module ActionMailer assert_enqueued_jobs number, only: [ ActionMailer::DeliveryJob, ActionMailer::Parameterized::DeliveryJob ], &block end + # Asserts that a specific email has been enqueued, optionally + # matching arguments. + # + # def test_email + # ContactMailer.welcome.deliver_later + # assert_enqueued_email_with ContactMailer, :welcome + # end + # + # def test_email_with_arguments + # ContactMailer.welcome("Hello", "Goodbye").deliver_later + # assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"] + # end + # + # If a block is passed, that block should cause the specified email + # to be enqueued. + # + # def test_email_in_block + # assert_enqueued_email_with ContactMailer, :welcome do + # ContactMailer.welcome.deliver_later + # end + # end + # + # If `args` is provided as a Hash, a parameterized email is matched. + # + # def test_parameterized_email + # assert_enqueued_email_with ContactMailer, :welcome, + # args: {email: 'user@example.com} do + # ContactMailer.with(email: 'user@example.com').welcome.deliver_later + # end + # end + def assert_enqueued_email_with(mailer, method, args: nil, queue: "mailers", &block) + if args.is_a? Hash + job = ActionMailer::Parameterized::DeliveryJob + args = [mailer.to_s, method.to_s, "deliver_now", args] + else + job = ActionMailer::DeliveryJob + args = [mailer.to_s, method.to_s, "deliver_now", *args] + end + + assert_enqueued_with(job: job, args: args, queue: queue, &block) + end + # Asserts that no emails are enqueued for later delivery. # # def test_no_emails diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb index abf50cf4da..5470d51599 100644 --- a/actionmailer/test/test_helper_test.rb +++ b/actionmailer/test/test_helper_test.rb @@ -10,6 +10,18 @@ class TestHelperMailer < ActionMailer::Base to: "test@example.com", from: "tester@example.com" end + + def test_args(recipient, name) + mail body: render(inline: "Hello, #{name}"), + to: recipient, + from: "tester@example.com" + end + + def test_parameter_args + mail body: render(inline: "All is #{params[:all]}"), + to: "test@example.com", + from: "tester@example.com" + end end class TestHelperMailerTest < ActionMailer::TestCase @@ -207,6 +219,36 @@ class TestHelperMailerTest < ActionMailer::TestCase assert_match(/0 .* but 1/, error.message) end + + def test_assert_enqueued_email_with + assert_nothing_raised do + assert_enqueued_email_with TestHelperMailer, :test do + silence_stream($stdout) do + TestHelperMailer.test.deliver_later + end + end + end + end + + def test_assert_enqueued_email_with_args + assert_nothing_raised do + assert_enqueued_email_with TestHelperMailer, :test_args, args: ["some_email", "some_name"] do + silence_stream($stdout) do + TestHelperMailer.test_args("some_email", "some_name").deliver_later + end + end + end + end + + def test_assert_enqueued_email_with_parameterized_args + assert_nothing_raised do + assert_enqueued_email_with TestHelperMailer, :test_parameter_args, args: { all: "good" } do + silence_stream($stdout) do + TestHelperMailer.with(all: "good").test_parameter_args.deliver_later + end + end + end + end end class AnotherTestHelperMailerTest < ActionMailer::TestCase -- cgit v1.2.3 From fbcc4bfe9a211e219da5d0bb01d894fcdaef0a0e Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Thu, 28 Sep 2017 20:04:46 +0200 Subject: Deprecate secret_token, long since usurped by secret_key_base. See the changelog entry. Remove `secrets.secret_token` from the bug report templates, since we don't accept bug reports for Rails versions that don't support a `secret_key_base`. [ claudiob & Kasper Timm Hansen ] --- activesupport/CHANGELOG.md | 12 ++++++++++ .../bug_report_templates/action_controller_gem.rb | 1 - .../action_controller_master.rb | 1 - railties/lib/rails/application.rb | 6 +++++ railties/test/application/configuration_test.rb | 26 ++++++++++++++++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 493ebeb01f..c95f95d076 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,15 @@ +* Deprecate `secrets.secret_token`. + + The architecture for secrets had a big upgrade between Rails 3 and Rails 4, + when the default changed from using `secret_token` to `secret_key_base`. + + `secret_token` has been soft deprecated in documentation for four years + but is still in place to support apps created before Rails 4. + Deprecation warnings have been added to help developers upgrade their + applications to `secret_key_base`. + + *claudiob*, *Kasper Timm Hansen* + * Return an instance of `HashWithIndifferentAccess` from `HashWithIndifferentAccess#transform_keys`. *Yuji Yaginuma* diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb index 341724cdcd..557b1d7bef 100644 --- a/guides/bug_report_templates/action_controller_gem.rb +++ b/guides/bug_report_templates/action_controller_gem.rb @@ -22,7 +22,6 @@ require "action_controller/railtie" class TestApp < Rails::Application config.root = __dir__ config.session_store :cookie_store, key: "cookie_store_key" - secrets.secret_token = "secret_token" secrets.secret_key_base = "secret_key_base" config.logger = Logger.new($stdout) diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb index 558d9bf3e2..cf76de80d2 100644 --- a/guides/bug_report_templates/action_controller_master.rb +++ b/guides/bug_report_templates/action_controller_master.rb @@ -20,7 +20,6 @@ require "action_controller/railtie" class TestApp < Rails::Application config.root = __dir__ - secrets.secret_token = "secret_token" secrets.secret_key_base = "secret_key_base" config.logger = Logger.new($stdout) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 24f5eeae87..4fd20185b1 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -6,6 +6,7 @@ require "active_support/core_ext/object/blank" require "active_support/key_generator" require "active_support/message_verifier" require "active_support/encrypted_configuration" +require "active_support/deprecation" require_relative "engine" require_relative "secrets" @@ -398,6 +399,11 @@ module Rails # Fallback to config.secret_token if secrets.secret_token isn't set secrets.secret_token ||= config.secret_token + if secrets.secret_token.present? + ActiveSupport::Deprecation.warn \ + "`secrets.secret_token` is deprecated in favor of `secret_key_base` and will be removed in Rails 6.0." + end + secrets end end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index c1a80eaeaf..bb8cc0876c 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -487,6 +487,32 @@ module ApplicationTests assert_equal "some_value", Rails.application.message_verifier(:sensitive_value).verify(message) end + test "config.secret_token is deprecated" do + app_file "config/initializers/secret_token.rb", <<-RUBY + Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" + RUBY + + app "production" + + assert_deprecated(/secret_token/) do + app.secrets + end + end + + test "secrets.secret_token is deprecated" do + app_file "config/secrets.yml", <<-YAML + production: + secret_token: "b3c631c314c0bbca50c1b2843150fe33" + YAML + + app "production" + + assert_deprecated(/secret_token/) do + app.secrets + end + end + + test "raises when secret_key_base is blank" do app_file "config/initializers/secret_token.rb", <<-RUBY Rails.application.credentials.secret_key_base = nil -- cgit v1.2.3 From f7b4be40410432b77e0c5d114b0ce73480ff984d Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Thu, 28 Sep 2017 21:46:39 +0200 Subject: Test for nil, people not likely to assign it false. --- actionpack/test/dispatch/cookies_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index fca3b24372..2051f7546f 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -899,8 +899,8 @@ class CookiesTest < ActionController::TestCase assert_nil @response.cookies["foo"] end - def test_use_authenticated_cookie_encryption_uses_legacy_hmac_aes_cbc_encrypiton - @request.env["action_dispatch.use_authenticated_cookie_encryption"] = false + def test_use_authenticated_cookie_encryption_uses_legacy_hmac_aes_cbc_encryption_when_not_enabled + @request.env["action_dispatch.use_authenticated_cookie_encryption"] = nil key_generator = @request.env["action_dispatch.key_generator"] encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] -- cgit v1.2.3 From d30586211b41e018869a1a3f4e3af778a31591db Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Thu, 28 Sep 2017 16:43:37 -0400 Subject: Preview PDFs and videos --- .travis.yml | 8 ++ .../controllers/active_storage/blobs_controller.rb | 15 +--- .../controllers/active_storage/disk_controller.rb | 6 +- .../active_storage/previews_controller.rb | 12 +++ .../active_storage/variants_controller.rb | 19 +---- activestorage/app/models/active_storage/blob.rb | 47 +++++++++-- activestorage/app/models/active_storage/preview.rb | 90 +++++++++++++++++++++ activestorage/app/models/active_storage/variant.rb | 6 +- .../app/models/active_storage/variation.rb | 9 +++ activestorage/config/routes.rb | 13 +++ activestorage/lib/active_storage.rb | 2 + activestorage/lib/active_storage/engine.rb | 10 +++ activestorage/lib/active_storage/previewer.rb | 69 ++++++++++++++++ .../lib/active_storage/previewer/pdf_previewer.rb | 17 ++++ .../active_storage/previewer/video_previewer.rb | 17 ++++ activestorage/lib/active_storage/service.rb | 7 ++ .../service/azure_storage_service.rb | 2 +- .../lib/active_storage/service/disk_service.rb | 5 +- .../lib/active_storage/service/gcs_service.rb | 11 ++- .../lib/active_storage/service/s3_service.rb | 2 +- .../test/controllers/blobs_controller_test.rb | 2 +- .../test/controllers/previews_controller_test.rb | 24 ++++++ .../test/controllers/variants_controller_test.rb | 4 +- activestorage/test/fixtures/files/report.pdf | Bin 0 -> 13469 bytes activestorage/test/fixtures/files/video.mp4 | Bin 0 -> 275433 bytes activestorage/test/models/preview_test.rb | 38 +++++++++ activestorage/test/models/variant_test.rb | 6 +- activestorage/test/previewer/pdf_previewer_test.rb | 20 +++++ .../test/previewer/video_previewer_test.rb | 20 +++++ .../test/service/azure_storage_service_test.rb | 2 +- activestorage/test/service/disk_service_test.rb | 2 +- activestorage/test/service/gcs_service_test.rb | 4 +- activestorage/test/service/mirror_service_test.rb | 6 +- activestorage/test/service/s3_service_test.rb | 2 +- activestorage/test/template/image_tag_test.rb | 8 +- activestorage/test/test_helper.rb | 7 +- 36 files changed, 444 insertions(+), 68 deletions(-) create mode 100644 activestorage/app/controllers/active_storage/previews_controller.rb create mode 100644 activestorage/app/models/active_storage/preview.rb create mode 100644 activestorage/lib/active_storage/previewer.rb create mode 100644 activestorage/lib/active_storage/previewer/pdf_previewer.rb create mode 100644 activestorage/lib/active_storage/previewer/video_previewer.rb create mode 100644 activestorage/test/controllers/previews_controller_test.rb create mode 100644 activestorage/test/fixtures/files/report.pdf create mode 100644 activestorage/test/fixtures/files/video.mp4 create mode 100644 activestorage/test/models/preview_test.rb create mode 100644 activestorage/test/previewer/pdf_previewer_test.rb create mode 100644 activestorage/test/previewer/video_previewer_test.rb diff --git a/.travis.yml b/.travis.yml index 27400b98c7..19164129c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,14 @@ services: addons: postgresql: "9.6" + apt: + sources: + - sourceline: "ppa:mc3man/trusty-media" + - sourceline: "ppa:ubuntuhandbook1/apps" + packages: + - ffmpeg + - mupdf + - mupdf-tools bundler_args: --without test --jobs 3 --retry 3 before_install: diff --git a/activestorage/app/controllers/active_storage/blobs_controller.rb b/activestorage/app/controllers/active_storage/blobs_controller.rb index 00aa8567c8..a17e3852f9 100644 --- a/activestorage/app/controllers/active_storage/blobs_controller.rb +++ b/activestorage/app/controllers/active_storage/blobs_controller.rb @@ -6,20 +6,11 @@ # authenticated redirection controller. class ActiveStorage::BlobsController < ActionController::Base def show - if blob = find_signed_blob - expires_in 5.minutes # service_url defaults to 5 minutes - redirect_to blob.service_url(disposition: disposition_param) + if blob = ActiveStorage::Blob.find_signed(params[:signed_id]) + expires_in ActiveStorage::Blob.service.url_expires_in + redirect_to blob.service_url(disposition: params[:disposition]) else head :not_found end end - - private - def find_signed_blob - ActiveStorage::Blob.find_signed(params[:signed_id]) - end - - def disposition_param - params[:disposition].presence_in(%w( inline attachment )) || "inline" - end end diff --git a/activestorage/app/controllers/active_storage/disk_controller.rb b/activestorage/app/controllers/active_storage/disk_controller.rb index 41e6d61bff..a4fd427cb2 100644 --- a/activestorage/app/controllers/active_storage/disk_controller.rb +++ b/activestorage/app/controllers/active_storage/disk_controller.rb @@ -8,7 +8,7 @@ class ActiveStorage::DiskController < ActionController::Base def show if key = decode_verified_key send_data disk_service.download(key), - disposition: disposition_param, content_type: params[:content_type] + disposition: params[:disposition], content_type: params[:content_type] else head :not_found end @@ -38,10 +38,6 @@ class ActiveStorage::DiskController < ActionController::Base ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key) end - def disposition_param - params[:disposition].presence || "inline" - end - def decode_verified_token ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token) diff --git a/activestorage/app/controllers/active_storage/previews_controller.rb b/activestorage/app/controllers/active_storage/previews_controller.rb new file mode 100644 index 0000000000..9e8cf27b6e --- /dev/null +++ b/activestorage/app/controllers/active_storage/previews_controller.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class ActiveStorage::PreviewsController < ActionController::Base + def show + if blob = ActiveStorage::Blob.find_signed(params[:signed_blob_id]) + expires_in ActiveStorage::Blob.service.url_expires_in + redirect_to ActiveStorage::Preview.new(blob, params[:variation_key]).processed.service_url(disposition: params[:disposition]) + else + head :not_found + end + end +end diff --git a/activestorage/app/controllers/active_storage/variants_controller.rb b/activestorage/app/controllers/active_storage/variants_controller.rb index 02e3010626..dc5e78ecc0 100644 --- a/activestorage/app/controllers/active_storage/variants_controller.rb +++ b/activestorage/app/controllers/active_storage/variants_controller.rb @@ -6,24 +6,11 @@ # authenticated redirection controller. class ActiveStorage::VariantsController < ActionController::Base def show - if blob = find_signed_blob - expires_in 5.minutes # service_url defaults to 5 minutes - redirect_to ActiveStorage::Variant.new(blob, decoded_variation).processed.service_url(disposition: disposition_param) + if blob = ActiveStorage::Blob.find_signed(params[:signed_blob_id]) + expires_in ActiveStorage::Blob.service.url_expires_in + redirect_to ActiveStorage::Variant.new(blob, params[:variation_key]).processed.service_url(disposition: params[:disposition]) else head :not_found end end - - private - def find_signed_blob - ActiveStorage::Blob.find_signed(params[:signed_blob_id]) - end - - def decoded_variation - ActiveStorage::Variation.decode(params[:variation_key]) - end - - def disposition_param - params[:disposition].presence_in(%w( inline attachment )) || "inline" - end end diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index e6cf08ce83..7477b09d09 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -14,6 +14,8 @@ # update a blob's metadata on a subsequent pass, but you should not update the key or change the uploaded file. # If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one. class ActiveStorage::Blob < ActiveRecord::Base + class UnpreviewableError < StandardError; end + self.table_name = "active_storage_blobs" has_secure_token :key @@ -21,6 +23,8 @@ class ActiveStorage::Blob < ActiveRecord::Base class_attribute :service + has_one_attached :preview_image + class << self # You can used the signed ID of a blob to refer to it on the client side without fear of tampering. # This is particularly helpful for direct uploads where the client-side needs to refer to the blob @@ -101,19 +105,18 @@ class ActiveStorage::Blob < ActiveRecord::Base content_type.start_with?("text") end - # Returns an ActiveStorage::Variant instance with the set of +transformations+ - # passed in. This is only relevant for image files, and it allows any image to - # be transformed for size, colors, and the like. Example: + # Returns an ActiveStorage::Variant instance with the set of +transformations+ provided. This is only relevant for image + # files, and it allows any image to be transformed for size, colors, and the like. Example: # # avatar.variant(resize: "100x100").processed.service_url # - # This will create and process a variant of the avatar blob that's constrained to a height and width of 100. + # This will create and process a variant of the avatar blob that's constrained to a height and width of 100px. # Then it'll upload said variant to the service according to a derivative key of the blob and the transformations. # # Frequently, though, you don't actually want to transform the variant right away. But rather simply refer to a # specific variant that can be created by a controller on-demand. Like so: # - # <%= image_tag url_for(Current.user.avatar.variant(resize: "100x100")) %> + # <%= image_tag Current.user.avatar.variant(resize: "100x100") %> # # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::VariantsController # can then produce on-demand. @@ -122,17 +125,45 @@ class ActiveStorage::Blob < ActiveRecord::Base end + # Returns an ActiveStorage::Preview instance with the set of +transformations+ provided. A preview is an image generated + # from a non-image blob. Active Storage comes with built-in previewers for videos and PDF documents. The video previewer + # extracts the first frame from a video and the PDF previewer extracts the first page from a PDF document. + # + # blob.preview(resize: "100x100").processed.service_url + # + # Avoid processing previews synchronously in views. Instead, link to a controller action that processes them on demand. + # Active Storage provides one, but you may want to create your own (for example, if you need authentication). Here’s + # how to use the built-in version: + # + # <%= image_tag video.preview(resize: "100x100") %> + # + # This method raises ActiveStorage::Blob::UnpreviewableError if no previewer accepts the receiving blob. To determine + # whether a blob is accepted by any previewer, call ActiveStorage::Blob#previewable?. + def preview(transformations) + if previewable? + ActiveStorage::Preview.new(self, ActiveStorage::Variation.new(transformations)) + else + raise UnpreviewableError + end + end + + # Returns true if any registered previewer accepts the blob. By default, this will return true for videos and PDF documents. + def previewable? + ActiveStorage.previewers.any? { |klass| klass.accept?(self) } + end + + # Returns the URL of the blob on the service. This URL is intended to be short-lived for security and not used directly # with users. Instead, the +service_url+ should only be exposed as a redirect from a stable, possibly authenticated URL. # Hiding the +service_url+ behind a redirect also gives you the power to change services without updating all URLs. And # it allows permanent URLs that redirect to the +service_url+ to be cached in the view. - def service_url(expires_in: 5.minutes, disposition: :inline) - service.url key, expires_in: expires_in, disposition: "#{disposition}; #{filename.parameters}", filename: filename, content_type: content_type + def service_url(expires_in: service.url_expires_in, disposition: "inline") + service.url key, expires_in: expires_in, disposition: disposition, filename: filename, content_type: content_type end # Returns a URL that can be used to directly upload a file for this blob on the service. This URL is intended to be # short-lived for security and only generated on-demand by the client-side JavaScript responsible for doing the uploading. - def service_url_for_direct_upload(expires_in: 5.minutes) + def service_url_for_direct_upload(expires_in: service.url_expires_in) service.url_for_direct_upload key, expires_in: expires_in, content_type: content_type, content_length: byte_size, checksum: checksum end diff --git a/activestorage/app/models/active_storage/preview.rb b/activestorage/app/models/active_storage/preview.rb new file mode 100644 index 0000000000..42c4bbc5a4 --- /dev/null +++ b/activestorage/app/models/active_storage/preview.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +# Some non-image blobs can be previewed: that is, they can be presented as images. A video blob can be previewed by +# extracting its first frame, and a PDF blob can be previewed by extracting its first page. +# +# A previewer extracts a preview image from a blob. Active Storage provides previewers for videos and PDFs: +# ActiveStorage::Previewer::VideoPreviewer and ActiveStorage::Previewer::PDFPreviewer. Build custom previewers by +# subclassing ActiveStorage::Previewer and implementing the requisite methods. Consult the ActiveStorage::Previewer +# documentation for more details on what's required of previewers. +# +# To choose the previewer for a blob, Active Storage calls +accept?+ on each registered previewer in order. It uses the +# first previewer for which +accept?+ returns true when given the blob. In a Rails application, add or remove previewers +# by manipulating +Rails.application.config.active_storage.previewers+ in an initializer: +# +# Rails.application.config.active_storage.previewers +# # => [ ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ] +# +# # Add a custom previewer for Microsoft Office documents: +# Rails.application.config.active_storage.previewers << DOCXPreviewer +# # => [ ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer, DOCXPreviewer ] +# +# Outside of a Rails application, modify +ActiveStorage.previewers+ instead. +# +# The built-in previewers rely on third-party system libraries: +# +# * {ffmpeg}[https://www.ffmpeg.org] +# * {mupdf}[https://mupdf.com] +# +# These libraries are not provided by Rails. You must install them yourself to use the built-in previewers. Before you +# install and use third-party software, make sure you understand the licensing implications of doing so. +class ActiveStorage::Preview + class UnprocessedError < StandardError; end + + attr_reader :blob, :variation + + def initialize(blob, variation_or_variation_key) + @blob, @variation = blob, ActiveStorage::Variation.wrap(variation_or_variation_key) + end + + # Processes the preview if it has not been processed yet. Returns the receiving Preview instance for convenience: + # + # blob.preview(resize: "100x100").processed.service_url + # + # Processing a preview generates an image from its blob and attaches the preview image to the blob. Because the preview + # image is stored with the blob, it is only generated once. + def processed + process unless processed? + self + end + + # Returns the blob's attached preview image. + def image + blob.preview_image + end + + # Returns the URL of the preview's variant on the service. Raises ActiveStorage::Preview::UnprocessedError if the + # preview has not been processed yet. + # + # This method synchronously processes a variant of the preview image, so do not call it in views. Instead, generate + # a stable URL that redirects to the short-lived URL returned by this method. + def service_url(**options) + if processed? + variant.service_url(options) + else + raise UnprocessedError + end + end + + private + def processed? + image.attached? + end + + def process + previewer.preview { |attachable| image.attach(attachable) } + end + + def variant + ActiveStorage::Variant.new(image, variation).processed + end + + + def previewer + previewer_class.new(blob) + end + + def previewer_class + ActiveStorage.previewers.detect { |klass| klass.accept?(blob) } + end +end diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb index 02bf32b352..54685b4c0e 100644 --- a/activestorage/app/models/active_storage/variant.rb +++ b/activestorage/app/models/active_storage/variant.rb @@ -38,8 +38,8 @@ class ActiveStorage::Variant attr_reader :blob, :variation delegate :service, to: :blob - def initialize(blob, variation) - @blob, @variation = blob, variation + def initialize(blob, variation_or_variation_key) + @blob, @variation = blob, ActiveStorage::Variation.wrap(variation_or_variation_key) end # Returns the variant instance itself after it's been processed or an existing processing has been found on the service. @@ -61,7 +61,7 @@ class ActiveStorage::Variant # Use url_for(variant) (or the implied form, like +link_to variant+ or +redirect_to variant+) to get the stable URL # for a variant that points to the ActiveStorage::VariantsController, which in turn will use this +service_call+ method # for its redirection. - def service_url(expires_in: 5.minutes, disposition: :inline) + def service_url(expires_in: service.url_expires_in, disposition: :inline) service.url key, expires_in: expires_in, disposition: disposition, filename: blob.filename, content_type: blob.content_type end diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb index cf04a879eb..df2643442a 100644 --- a/activestorage/app/models/active_storage/variation.rb +++ b/activestorage/app/models/active_storage/variation.rb @@ -13,6 +13,15 @@ class ActiveStorage::Variation attr_reader :transformations class << self + def wrap(variation_or_key) + case variation_or_key + when self + variation_or_key + else + decode variation_or_key + end + end + # Returns a variation instance with the transformations that were encoded by +encode+. def decode(key) new ActiveStorage.verifier.verify(key, purpose: :variation) diff --git a/activestorage/config/routes.rb b/activestorage/config/routes.rb index c3194887be..c659e079fd 100644 --- a/activestorage/config/routes.rb +++ b/activestorage/config/routes.rb @@ -24,6 +24,19 @@ Rails.application.routes.draw do resolve("ActiveStorage::Variant") { |variant| route_for(:rails_variant, variant) } + get "/rails/active_storage/previews/:signed_blob_id/:variation_key/*filename" => "active_storage/previews#show", as: :rails_blob_preview, internal: true + + direct :rails_preview do |preview| + signed_blob_id = preview.blob.signed_id + variation_key = preview.variation.key + filename = preview.blob.filename + + route_for(:rails_blob_preview, signed_blob_id, variation_key, filename) + end + + resolve("ActiveStorage::Preview") { |preview| route_for(:rails_preview, preview) } + + get "/rails/active_storage/disk/:encoded_key/*filename" => "active_storage/disk#show", as: :rails_disk_service, internal: true put "/rails/active_storage/disk/:encoded_token" => "active_storage/disk#update", as: :update_rails_disk_service, internal: true post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads, internal: true diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb index ccc1d4a163..44d9c25504 100644 --- a/activestorage/lib/active_storage.rb +++ b/activestorage/lib/active_storage.rb @@ -33,6 +33,8 @@ module ActiveStorage autoload :Attached autoload :Service + autoload :Previewer mattr_accessor :verifier + mattr_accessor :previewers, default: [] end diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb index 590a36a30a..335eae8dd8 100644 --- a/activestorage/lib/active_storage/engine.rb +++ b/activestorage/lib/active_storage/engine.rb @@ -3,11 +3,15 @@ require "rails" require "active_storage" +require "active_storage/previewer/pdf_previewer" +require "active_storage/previewer/video_previewer" + module ActiveStorage class Engine < Rails::Engine # :nodoc: isolate_namespace ActiveStorage config.active_storage = ActiveSupport::OrderedOptions.new + config.active_storage.previewers = [ ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ] config.eager_load_namespaces << ActiveStorage @@ -59,5 +63,11 @@ module ActiveStorage end end end + + initializer "active_storage.previewers" do + config.after_initialize do |app| + ActiveStorage.previewers = app.config.active_storage.previewers || [] + end + end end end diff --git a/activestorage/lib/active_storage/previewer.rb b/activestorage/lib/active_storage/previewer.rb new file mode 100644 index 0000000000..c91f64ac65 --- /dev/null +++ b/activestorage/lib/active_storage/previewer.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module ActiveStorage + # This is an abstract base class for previewers, which generate images from blobs. See + # ActiveStorage::Previewer::PDFPreviewer and ActiveStorage::Previewer::VideoPreviewer for examples of + # concrete subclasses. + class Previewer + attr_reader :blob + + # Implement this method in a concrete subclass. Have it return true when given a blob from which + # the previewer can generate an image. + def self.accept?(blob) + false + end + + def initialize(blob) + @blob = blob + end + + # Override this method in a concrete subclass. Have it yield an attachable preview image (i.e. + # anything accepted by ActiveStorage::Attached::One#attach). + def preview + raise NotImplementedError + end + + private + # Downloads the blob to a new tempfile. Yields the tempfile. + # + # Use this method to get a tempfile that you can provide to a drawing command. + def open # :doc: + Tempfile.open("input") do |file| + download_blob_to file + yield file + end + end + + def download_blob_to(file) + file.binmode + blob.download { |chunk| file.write(chunk) } + file.rewind + end + + + # Executes a system command, capturing its binary output in a tempfile. Yields the tempfile. + # + # Use this method to shell out to system libraries (e.g. mupdf or ffmpeg) for preview image + # generation. The resulting tempfile can be used as the +:io+ value in an attachable Hash: + # + # def preview + # open do |input| + # draw "my-drawing-command", input.path, "--format", "png", "-" do |output| + # yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" + # end + # end + # end + def draw(*argv) # :doc: + Tempfile.open("output") do |file| + capture *argv, to: file + yield file + end + end + + def capture(*argv, to:) + to.binmode + IO.popen(argv) { |out| IO.copy_stream(out, to) } + to.rewind + end + end +end diff --git a/activestorage/lib/active_storage/previewer/pdf_previewer.rb b/activestorage/lib/active_storage/previewer/pdf_previewer.rb new file mode 100644 index 0000000000..31a2a8f120 --- /dev/null +++ b/activestorage/lib/active_storage/previewer/pdf_previewer.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module ActiveStorage + class Previewer::PDFPreviewer < Previewer + def self.accept?(blob) + blob.content_type == "application/pdf" + end + + def preview + open do |input| + draw "mutool", "draw", "-F", "png", "-o", "-", input.path, "1" do |output| + yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" + end + end + end + end +end diff --git a/activestorage/lib/active_storage/previewer/video_previewer.rb b/activestorage/lib/active_storage/previewer/video_previewer.rb new file mode 100644 index 0000000000..840d87f100 --- /dev/null +++ b/activestorage/lib/active_storage/previewer/video_previewer.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module ActiveStorage + class Previewer::VideoPreviewer < Previewer + def self.accept?(blob) + blob.video? + end + + def preview + open do |input| + draw "ffmpeg", "-i", input.path, "-y", "-vcodec", "png", "-vf", "thumbnail", "-vframes", "1", "-f", "image2", "-" do |output| + yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" + end + end + end + end +end diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb index b80fdea1ab..1f012da1e7 100644 --- a/activestorage/lib/active_storage/service.rb +++ b/activestorage/lib/active_storage/service.rb @@ -4,6 +4,7 @@ require "active_storage/log_subscriber" module ActiveStorage class IntegrityError < StandardError; end + # Abstract class serving as an interface for concrete services. # # The available services are: @@ -42,6 +43,8 @@ module ActiveStorage class_attribute :logger + class_attribute :url_expires_in, default: 5.minutes + class << self # Configure an Active Storage service by name from a set of configurations, # typically loaded from a YAML file. The Active Storage engine uses this @@ -113,5 +116,9 @@ module ActiveStorage # ActiveStorage::Service::DiskService => Disk self.class.name.split("::").third.remove("Service") end + + def content_disposition_with(type: "inline", filename:) + (type.to_s.presence_in(%w( attachment inline )) || "inline") + "; #{filename.parameters}" + end end end diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb index 895cc9c2f1..27dd192ce6 100644 --- a/activestorage/lib/active_storage/service/azure_storage_service.rb +++ b/activestorage/lib/active_storage/service/azure_storage_service.rb @@ -66,7 +66,7 @@ module ActiveStorage URI(base_url), false, permissions: "r", expiry: format_expiry(expires_in), - content_disposition: disposition, + content_disposition: content_disposition_with(type: disposition, filename: filename), content_type: content_type ).to_s diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb index f600753a08..52eaba4e7b 100644 --- a/activestorage/lib/active_storage/service/disk_service.rb +++ b/activestorage/lib/active_storage/service/disk_service.rb @@ -64,9 +64,10 @@ module ActiveStorage if defined?(Rails.application) Rails.application.routes.url_helpers.rails_disk_service_path \ verified_key_with_expiration, - filename: filename, disposition: disposition, content_type: content_type + filename: filename, disposition: content_disposition_with(type: disposition, filename: filename), content_type: content_type else - "/rails/active_storage/disk/#{verified_key_with_expiration}/#{filename}?content_type=#{content_type}&disposition=#{disposition}" + "/rails/active_storage/disk/#{verified_key_with_expiration}/#{filename}?content_type=#{content_type}" \ + "&disposition=#{content_disposition_with(type: disposition, filename: filename)}" end payload[:url] = generated_url diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb index 685dd61a0a..b4ffeeeb8a 100644 --- a/activestorage/lib/active_storage/service/gcs_service.rb +++ b/activestorage/lib/active_storage/service/gcs_service.rb @@ -24,12 +24,17 @@ module ActiveStorage end end - # FIXME: Add streaming when given a block + # FIXME: Download in chunks when given a block. def download(key) instrument :download, key do io = file_for(key).download io.rewind - io.read + + if block_given? + yield io.read + else + io.read + end end end @@ -54,7 +59,7 @@ module ActiveStorage def url(key, expires_in:, filename:, content_type:, disposition:) instrument :url, key do |payload| generated_url = file_for(key).signed_url expires: expires_in, query: { - "response-content-disposition" => disposition, + "response-content-disposition" => content_disposition_with(type: disposition, filename: filename), "response-content-type" => content_type } diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb index e074269353..3e93cdd072 100644 --- a/activestorage/lib/active_storage/service/s3_service.rb +++ b/activestorage/lib/active_storage/service/s3_service.rb @@ -55,7 +55,7 @@ module ActiveStorage def url(key, expires_in:, filename:, disposition:, content_type:) instrument :url, key do |payload| generated_url = object_for(key).presigned_url :get, expires_in: expires_in.to_i, - response_content_disposition: disposition, + response_content_disposition: content_disposition_with(type: disposition, filename: filename), response_content_type: content_type payload[:url] = generated_url diff --git a/activestorage/test/controllers/blobs_controller_test.rb b/activestorage/test/controllers/blobs_controller_test.rb index c37b9c8a10..97177e64c2 100644 --- a/activestorage/test/controllers/blobs_controller_test.rb +++ b/activestorage/test/controllers/blobs_controller_test.rb @@ -5,7 +5,7 @@ require "database/setup" class ActiveStorage::BlobsControllerTest < ActionDispatch::IntegrationTest setup do - @blob = create_image_blob filename: "racecar.jpg" + @blob = create_file_blob filename: "racecar.jpg" end test "showing blob utilizes browser caching" do diff --git a/activestorage/test/controllers/previews_controller_test.rb b/activestorage/test/controllers/previews_controller_test.rb new file mode 100644 index 0000000000..c3151a710e --- /dev/null +++ b/activestorage/test/controllers/previews_controller_test.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +class ActiveStorage::PreviewsControllerTest < ActionDispatch::IntegrationTest + setup do + @blob = create_file_blob filename: "report.pdf", content_type: "application/pdf" + end + + test "showing preview inline" do + get rails_blob_preview_url( + filename: @blob.filename, + signed_blob_id: @blob.signed_id, + variation_key: ActiveStorage::Variation.encode(resize: "100x100")) + + assert @blob.preview_image.attached? + assert_redirected_to(/report\.png\?.*disposition=inline/) + + image = read_image(@blob.preview_image.variant(resize: "100x100")) + assert_equal 77, image.width + assert_equal 100, image.height + end +end diff --git a/activestorage/test/controllers/variants_controller_test.rb b/activestorage/test/controllers/variants_controller_test.rb index 0a049f3bc4..6c70d73786 100644 --- a/activestorage/test/controllers/variants_controller_test.rb +++ b/activestorage/test/controllers/variants_controller_test.rb @@ -5,7 +5,7 @@ require "database/setup" class ActiveStorage::VariantsControllerTest < ActionDispatch::IntegrationTest setup do - @blob = create_image_blob filename: "racecar.jpg" + @blob = create_file_blob filename: "racecar.jpg" end test "showing variant inline" do @@ -16,7 +16,7 @@ class ActiveStorage::VariantsControllerTest < ActionDispatch::IntegrationTest assert_redirected_to(/racecar\.jpg\?.*disposition=inline/) - image = read_image_variant(@blob.variant(resize: "100x100")) + image = read_image(@blob.variant(resize: "100x100")) assert_equal 100, image.width assert_equal 67, image.height end diff --git a/activestorage/test/fixtures/files/report.pdf b/activestorage/test/fixtures/files/report.pdf new file mode 100644 index 0000000000..cccb9b5d64 Binary files /dev/null and b/activestorage/test/fixtures/files/report.pdf differ diff --git a/activestorage/test/fixtures/files/video.mp4 b/activestorage/test/fixtures/files/video.mp4 new file mode 100644 index 0000000000..8fb1c5b24d Binary files /dev/null and b/activestorage/test/fixtures/files/video.mp4 differ diff --git a/activestorage/test/models/preview_test.rb b/activestorage/test/models/preview_test.rb new file mode 100644 index 0000000000..317a2d5c58 --- /dev/null +++ b/activestorage/test/models/preview_test.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +class ActiveStorage::PreviewTest < ActiveSupport::TestCase + test "previewing a PDF" do + blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf") + preview = blob.preview(resize: "640x280").processed + assert preview.image.attached? + assert_equal "report.png", preview.image.filename.to_s + assert_equal "image/png", preview.image.content_type + + image = read_image(preview.image) + assert_equal 612, image.width + assert_equal 792, image.height + end + + test "previewing an MP4 video" do + blob = create_file_blob(filename: "video.mp4", content_type: "video/mp4") + preview = blob.preview(resize: "640x280").processed + assert preview.image.attached? + assert_equal "video.png", preview.image.filename.to_s + assert_equal "image/png", preview.image.content_type + + image = read_image(preview.image) + assert_equal 640, image.width + assert_equal 480, image.height + end + + test "previewing an unpreviewable blob" do + blob = create_file_blob + + assert_raises ActiveStorage::Blob::UnpreviewableError do + blob.preview resize: "640x280" + end + end +end diff --git a/activestorage/test/models/variant_test.rb b/activestorage/test/models/variant_test.rb index ca112ab907..d7cbef4e55 100644 --- a/activestorage/test/models/variant_test.rb +++ b/activestorage/test/models/variant_test.rb @@ -5,14 +5,14 @@ require "database/setup" class ActiveStorage::VariantTest < ActiveSupport::TestCase setup do - @blob = create_image_blob filename: "racecar.jpg" + @blob = create_file_blob filename: "racecar.jpg" end test "resized variation" do variant = @blob.variant(resize: "100x100").processed assert_match(/racecar\.jpg/, variant.service_url) - image = read_image_variant(variant) + image = read_image(variant) assert_equal 100, image.width assert_equal 67, image.height end @@ -21,7 +21,7 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase variant = @blob.variant(resize: "100x100", monochrome: true).processed assert_match(/racecar\.jpg/, variant.service_url) - image = read_image_variant(variant) + image = read_image(variant) assert_equal 100, image.width assert_equal 67, image.height assert_match(/Gray/, image.colorspace) diff --git a/activestorage/test/previewer/pdf_previewer_test.rb b/activestorage/test/previewer/pdf_previewer_test.rb new file mode 100644 index 0000000000..60f075d1b2 --- /dev/null +++ b/activestorage/test/previewer/pdf_previewer_test.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "active_storage/previewer/pdf_previewer" + +class ActiveStorage::Previewer::PDFPreviewerTest < ActiveSupport::TestCase + setup do + @blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf") + end + + test "previewing a PDF document" do + ActiveStorage::Previewer::PDFPreviewer.new(@blob).preview do |attachable| + assert_equal "image/png", attachable[:content_type] + assert_equal "report.png", attachable[:filename] + + image = MiniMagick::Image.read(attachable[:io]) + assert_equal 612, image.width + assert_equal 792, image.height + end + end +end diff --git a/activestorage/test/previewer/video_previewer_test.rb b/activestorage/test/previewer/video_previewer_test.rb new file mode 100644 index 0000000000..967d5d5ba9 --- /dev/null +++ b/activestorage/test/previewer/video_previewer_test.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "active_storage/previewer/video_previewer" + +class ActiveStorage::Previewer::VideoPreviewerTest < ActiveSupport::TestCase + setup do + @blob = create_file_blob(filename: "video.mp4", content_type: "video/mp4") + end + + test "previewing an MP4 video" do + ActiveStorage::Previewer::VideoPreviewer.new(@blob).preview do |attachable| + assert_equal "image/png", attachable[:content_type] + assert_equal "video.png", attachable[:filename] + + image = MiniMagick::Image.read(attachable[:io]) + assert_equal 640, image.width + assert_equal 480, image.height + end + end +end diff --git a/activestorage/test/service/azure_storage_service_test.rb b/activestorage/test/service/azure_storage_service_test.rb index 4729bdfbc5..4b7e70b8b1 100644 --- a/activestorage/test/service/azure_storage_service_test.rb +++ b/activestorage/test/service/azure_storage_service_test.rb @@ -11,7 +11,7 @@ if SERVICE_CONFIGURATIONS[:azure] test "signed URL generation" do url = @service.url(FIXTURE_KEY, expires_in: 5.minutes, - disposition: "inline; filename=\"avatar.png\"", filename: "avatar.png", content_type: "image/png") + disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png") assert_match(/(\S+)&rscd=inline%3B\+filename%3D%22avatar.png%22&rsct=image%2Fpng/, url) assert_match SERVICE_CONFIGURATIONS[:azure][:container], url diff --git a/activestorage/test/service/disk_service_test.rb b/activestorage/test/service/disk_service_test.rb index e07d1d88bc..4a6361b920 100644 --- a/activestorage/test/service/disk_service_test.rb +++ b/activestorage/test/service/disk_service_test.rb @@ -9,6 +9,6 @@ class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase test "url generation" do assert_match(/rails\/active_storage\/disk\/.*\/avatar\.png\?content_type=image%2Fpng&disposition=inline/, - @service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: "inline; filename=\"avatar.png\"", filename: "avatar.png", content_type: "image/png")) + @service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png")) end end diff --git a/activestorage/test/service/gcs_service_test.rb b/activestorage/test/service/gcs_service_test.rb index f664cee90b..5566c664a9 100644 --- a/activestorage/test/service/gcs_service_test.rb +++ b/activestorage/test/service/gcs_service_test.rb @@ -34,10 +34,10 @@ if SERVICE_CONFIGURATIONS[:gcs] test "signed URL generation" do freeze_time do url = SERVICE.bucket.signed_url(FIXTURE_KEY, expires: 120) + - "&response-content-disposition=inline%3B+filename%3D%22test.txt%22" + + "&response-content-disposition=inline%3B+filename%3D%22test.txt%22%3B+filename%2A%3DUTF-8%27%27test.txt" + "&response-content-type=text%2Fplain" - assert_equal url, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: "inline; filename=\"test.txt\"", filename: "test.txt", content_type: "text/plain") + assert_equal url, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain") end end end diff --git a/activestorage/test/service/mirror_service_test.rb b/activestorage/test/service/mirror_service_test.rb index 93e86eff70..92101b1282 100644 --- a/activestorage/test/service/mirror_service_test.rb +++ b/activestorage/test/service/mirror_service_test.rb @@ -47,9 +47,11 @@ class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase end test "URL generation in primary service" do + filename = ActiveStorage::Filename.new("test.txt") + freeze_time do - assert_equal SERVICE.primary.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain"), - @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain") + assert_equal SERVICE.primary.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: filename, content_type: "text/plain"), + @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: filename, content_type: "text/plain") end end diff --git a/activestorage/test/service/s3_service_test.rb b/activestorage/test/service/s3_service_test.rb index c07d6396b1..c3818422aa 100644 --- a/activestorage/test/service/s3_service_test.rb +++ b/activestorage/test/service/s3_service_test.rb @@ -33,7 +33,7 @@ if SERVICE_CONFIGURATIONS[:s3] && SERVICE_CONFIGURATIONS[:s3][:access_key_id].pr test "signed URL generation" do url = @service.url(FIXTURE_KEY, expires_in: 5.minutes, - disposition: "inline; filename=\"avatar.png\"", filename: "avatar.png", content_type: "image/png") + disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png") assert_match(/s3\.(\S+)?amazonaws.com.*response-content-disposition=inline.*avatar\.png.*response-content-type=image%2Fpng/, url) assert_match SERVICE_CONFIGURATIONS[:s3][:bucket], url diff --git a/activestorage/test/template/image_tag_test.rb b/activestorage/test/template/image_tag_test.rb index dedc58452e..80f183e0e3 100644 --- a/activestorage/test/template/image_tag_test.rb +++ b/activestorage/test/template/image_tag_test.rb @@ -7,7 +7,7 @@ class ActiveStorage::ImageTagTest < ActionView::TestCase tests ActionView::Helpers::AssetTagHelper setup do - @blob = create_image_blob filename: "racecar.jpg" + @blob = create_file_blob filename: "racecar.jpg" end test "blob" do @@ -19,6 +19,12 @@ class ActiveStorage::ImageTagTest < ActionView::TestCase assert_dom_equal %(), image_tag(variant) end + test "preview" do + blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf") + preview = blob.preview(resize: "100x100") + assert_dom_equal %(), image_tag(preview) + end + test "attachment" do attachment = ActiveStorage::Attachment.new(blob: @blob) assert_dom_equal %(), image_tag(attachment) diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb index 2a969fa326..dd37239060 100644 --- a/activestorage/test/test_helper.rb +++ b/activestorage/test/test_helper.rb @@ -6,6 +6,7 @@ require "bundler/setup" require "active_support" require "active_support/test_case" require "active_support/testing/autorun" +require "mini_magick" begin require "byebug" @@ -44,7 +45,7 @@ class ActiveSupport::TestCase ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type end - def create_image_blob(filename: "racecar.jpg", content_type: "image/jpeg") + def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg") ActiveStorage::Blob.create_after_upload! \ io: file_fixture(filename).open, filename: filename, content_type: content_type @@ -54,8 +55,8 @@ class ActiveSupport::TestCase ActiveStorage::Blob.create_before_direct_upload! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type end - def read_image_variant(variant) - MiniMagick::Image.open variant.service.send(:path_for, variant.key) + def read_image(blob_or_variant) + MiniMagick::Image.open blob_or_variant.service.send(:path_for, blob_or_variant.key) end end -- cgit v1.2.3 From 7c68fc98d8f67ee55b8cedadad2bdd3a7788dbf3 Mon Sep 17 00:00:00 2001 From: Conrad Beach Date: Thu, 28 Sep 2017 16:51:09 -0600 Subject: [ci skip] Fix typo. --- guides/source/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/testing.md b/guides/source/testing.md index 4ee3267261..c5b2a694e7 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -1513,7 +1513,7 @@ class BillingJobTest < ActiveJob::TestCase end ``` -This test is pretty simple and only asserts that the job get the work done +This test is pretty simple and only asserts that the job got the work done as expected. By default, `ActiveJob::TestCase` will set the queue adapter to `:test` so that -- cgit v1.2.3 From 4a13c1e1d2496657f35a7a35092d1b403fe03526 Mon Sep 17 00:00:00 2001 From: Kazunori Kajihiro Date: Fri, 29 Sep 2017 18:15:56 +0900 Subject: Yield with an error instance instead of error class --- activejob/lib/active_job/exceptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb index dfc74deb1a..8b4a88ba6a 100644 --- a/activejob/lib/active_job/exceptions.rb +++ b/activejob/lib/active_job/exceptions.rb @@ -49,7 +49,7 @@ module ActiveJob retry_job wait: determine_delay(wait), queue: queue, priority: priority else if block_given? - yield self, exception + yield self, error else logger.error "Stopped retrying #{self.class} due to a #{exception}, which reoccurred on #{executions} attempts. The original exception was #{error.cause.inspect}." raise error -- cgit v1.2.3 From 83b7cb3a4711794ca1716d1b593643d2309f0019 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 30 Sep 2017 05:39:27 +0900 Subject: Fix "warning: `*' interpreted as argument prefix" --- activestorage/lib/active_storage/previewer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/lib/active_storage/previewer.rb b/activestorage/lib/active_storage/previewer.rb index c91f64ac65..930b376067 100644 --- a/activestorage/lib/active_storage/previewer.rb +++ b/activestorage/lib/active_storage/previewer.rb @@ -55,7 +55,7 @@ module ActiveStorage # end def draw(*argv) # :doc: Tempfile.open("output") do |file| - capture *argv, to: file + capture(*argv, to: file) yield file end end -- cgit v1.2.3 From 325c06fbc499aa4da1ce50d9b85dbf5c6ed3321e Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 30 Sep 2017 05:48:23 +0900 Subject: Fix `test "signed URL generation"` failure https://travis-ci.org/rails/rails/jobs/281044755#L5582-L5586 --- activestorage/test/service/azure_storage_service_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/test/service/azure_storage_service_test.rb b/activestorage/test/service/azure_storage_service_test.rb index 4b7e70b8b1..be31bbe858 100644 --- a/activestorage/test/service/azure_storage_service_test.rb +++ b/activestorage/test/service/azure_storage_service_test.rb @@ -13,7 +13,7 @@ if SERVICE_CONFIGURATIONS[:azure] url = @service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png") - assert_match(/(\S+)&rscd=inline%3B\+filename%3D%22avatar.png%22&rsct=image%2Fpng/, url) + assert_match(/(\S+)&rscd=inline%3B\+filename%3D%22avatar\.png%22%3B\+filename\*%3DUTF-8%27%27avatar\.png&rsct=image%2Fpng/, url) assert_match SERVICE_CONFIGURATIONS[:azure][:container], url end end -- cgit v1.2.3 From 8154c34805cdac7021b779742ea1c6f0fb560dfa Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 30 Sep 2017 08:48:13 +0900 Subject: Fix "warning: method redefined; discarding old test_scaffold_plural_names" Since warning was issued due to duplication of test names, fix the test name to a meaningful name. This fixes following warnings: ``` railties/test/generators/named_base_test.rb:141: warning: method redefined; discarding old test_scaffold_plural_names railties/test/generators/named_base_test.rb:62: warning: previous definition of test_scaffold_plural_names was here ``` --- railties/test/generators/named_base_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 64e9909859..967754c813 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -59,7 +59,7 @@ class NamedBaseTest < Rails::Generators::TestCase ActiveRecord::Base.pluralize_table_names = original_pluralize_table_names end - def test_scaffold_plural_names + def test_namespaced_scaffold_plural_names g = generator ["admin/foo"] assert_name g, "admin/foos", :controller_name assert_name g, %w(admin), :controller_class_path @@ -69,7 +69,7 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "admin.foos", :controller_i18n_scope end - def test_scaffold_plural_names_as_ruby + def test_namespaced_scaffold_plural_names_as_ruby g = generator ["Admin::Foo"] assert_name g, "Admin::Foos", :controller_name assert_name g, %w(admin), :controller_class_path -- cgit v1.2.3 From 5d149048ffa56c8fa1c41d1ef3bcf9472052f369 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 30 Sep 2017 09:06:48 +0900 Subject: Don't use Active Support where it is not needed. This code has been changed with https://github.com/rails/rails/pull/30735/files#diff-8e5f6b33c191ad6dec07f3288345a13fL47. However, `active_support/time` is not load automatically, so if use Action Pack alone, `days` method can not use and an error occurs. In this case, I think that there is no problem by specifying a value with Integer. --- actionpack/lib/action_dispatch/middleware/ssl.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb index 9e1684f689..ef633aadc6 100644 --- a/actionpack/lib/action_dispatch/middleware/ssl.rb +++ b/actionpack/lib/action_dispatch/middleware/ssl.rb @@ -49,7 +49,7 @@ module ActionDispatch # Default to 180 days, the low end for https://www.ssllabs.com/ssltest/ # and greater than the 18-week requirement for browser preload lists. - HSTS_EXPIRES_IN = 180.days.to_i + HSTS_EXPIRES_IN = 15552000 def self.default_hsts_options { expires: HSTS_EXPIRES_IN, subdomains: true, preload: false } -- cgit v1.2.3 From 35dc49e517acff12ad249995cfbac41334d30d66 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 30 Sep 2017 17:38:27 +0900 Subject: Remove unused methods from `RenderPartialWithRecordIdentificationController` These methods no longer used since a3da293. --- .../render_partial_with_record_identification_test.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/actionview/test/activerecord/render_partial_with_record_identification_test.rb b/actionview/test/activerecord/render_partial_with_record_identification_test.rb index 367d2c3174..3a698fa42e 100644 --- a/actionview/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionview/test/activerecord/render_partial_with_record_identification_test.rb @@ -17,21 +17,11 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base render partial: Reply.base end - def render_with_has_many_through_association - @developer = Developer.first - render partial: @developer.topics - end - def render_with_has_one_association @company = Company.find(1) render partial: @company.mascot end - def render_with_belongs_to_association - @reply = Reply.find(1) - render partial: @reply.topic - end - def render_with_record @developer = Developer.first render partial: @developer -- cgit v1.2.3 From d39879435c51af1b9966f9037a9962146ff1ea71 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 30 Sep 2017 18:01:56 +0900 Subject: Testing to ensure both bang and non-bang methods behaves consistently Follow up of #30728. --- .../test/hash_with_indifferent_access_test.rb | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb index b878ac20fa..41d653fa59 100644 --- a/activesupport/test/hash_with_indifferent_access_test.rb +++ b/activesupport/test/hash_with_indifferent_access_test.rb @@ -406,6 +406,29 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash end + def test_indifferent_transform_keys_bang + indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) + indifferent_strings.transform_keys! { |k| k * 2 } + + assert_equal({ "aa" => 1, "bb" => 2 }, indifferent_strings) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings + end + + def test_indifferent_transform_values + hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).transform_values { |v| v * 2 } + + assert_equal({ "a" => 2, "b" => 4 }, hash) + assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash + end + + def test_indifferent_transform_values_bang + indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings) + indifferent_strings.transform_values! { |v| v * 2 } + + assert_equal({ "a" => 2, "b" => 4 }, indifferent_strings) + 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) -- cgit v1.2.3 From 5349f231089ddb884c7cb5405e74b7871383d65d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 30 Sep 2017 18:33:53 +0900 Subject: Remove `:api:` tag that has leaked on the doc directly [ci skip] Currently `:api:` tag has leaked on the doc directly since RDoc doesn't support `:api:` tag directive. http://api.rubyonrails.org/v5.1/classes/AbstractController/Rendering.html So `:api: private` doesn't work as expected. We are using `:nodoc:` for the purpose. Related #13989. --- actionpack/lib/abstract_controller/base.rb | 2 -- actionpack/lib/abstract_controller/rendering.rb | 16 +++------------- .../lib/action_controller/metal/instrumentation.rb | 4 ---- actionpack/test/controller/new_base/base_test.rb | 3 --- actionview/lib/action_view/context.rb | 2 -- actionview/lib/action_view/rendering.rb | 6 +----- 6 files changed, 4 insertions(+), 29 deletions(-) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 3761054bb7..14afb355ab 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -180,8 +180,6 @@ module AbstractController # # ==== Parameters # * name - The name of an action to be tested - # - # :api: private def action_method?(name) self.class.action_methods.include?(name) end diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 41898c4c2e..4b4915a85d 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -20,7 +20,6 @@ module AbstractController # Normalizes arguments, options and then delegates render_to_body and # sticks the result in self.response_body. - # :api: public def render(*args, &block) options = _normalize_render(*args, &block) rendered_body = render_to_body(options) @@ -42,19 +41,16 @@ module AbstractController # (as ActionController extends it to be anything that # responds to the method each), this method needs to be # overridden in order to still return a string. - # :api: plugin def render_to_string(*args, &block) options = _normalize_render(*args, &block) render_to_body(options) end # Performs the actual template rendering. - # :api: public def render_to_body(options = {}) end - # Returns Content-Type of rendered content - # :api: public + # Returns Content-Type of rendered content. def rendered_format Mime[:text] end @@ -65,7 +61,6 @@ module AbstractController # This method should return a hash with assigns. # You can overwrite this configuration per controller. - # :api: public def view_assigns protected_vars = _protected_ivars variables = instance_variables @@ -79,7 +74,6 @@ module AbstractController # Normalize args by converting render "foo" to # render :action => "foo" and render "foo/bar" to # render :file => "foo/bar". - # :api: plugin def _normalize_args(action = nil, options = {}) if action.respond_to?(:permitted?) if action.permitted? @@ -95,20 +89,17 @@ module AbstractController end # Normalize options. - # :api: plugin def _normalize_options(options) options end # Process extra options. - # :api: plugin def _process_options(options) options end # Process the rendered format. - # :api: private - def _process_format(format) + def _process_format(format) # :nodoc: end def _process_variant(options) @@ -121,8 +112,7 @@ module AbstractController end # Normalize args and options. - # :api: private - def _normalize_render(*args, &block) + def _normalize_render(*args, &block) # :nodoc: options = _normalize_args(*args, &block) _process_variant(options) _normalize_options(options) diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index 476f0843b2..5ef83af07a 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -83,15 +83,12 @@ module ActionController # def cleanup_view_runtime # super - time_taken_in_something_expensive # end - # - # :api: plugin 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) payload[:view_runtime] = view_runtime end @@ -100,7 +97,6 @@ module ActionController # A hook which allows other frameworks to log what happened during # controller process action. This method should return an array # with the messages to be added. - # :api: plugin def log_process_action(payload) #:nodoc: messages, view_runtime = [], payload[:view_runtime] messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime diff --git a/actionpack/test/controller/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb index d9f200b2a7..280134f8d2 100644 --- a/actionpack/test/controller/new_base/base_test.rb +++ b/actionpack/test/controller/new_base/base_test.rb @@ -47,7 +47,6 @@ module Dispatching end class BaseTest < Rack::TestCase - # :api: plugin test "simple dispatching" do get "/dispatching/simple/index" @@ -56,14 +55,12 @@ module Dispatching assert_content_type "text/plain; charset=utf-8" end - # :api: plugin test "directly modifying response body" do get "/dispatching/simple/modify_response_body" assert_body "success" end - # :api: plugin test "directly modifying response body twice" do get "/dispatching/simple/modify_response_body_twice" diff --git a/actionview/lib/action_view/context.rb b/actionview/lib/action_view/context.rb index e1b02fbde4..665a9e3171 100644 --- a/actionview/lib/action_view/context.rb +++ b/actionview/lib/action_view/context.rb @@ -19,7 +19,6 @@ module ActionView attr_accessor :output_buffer, :view_flow # Prepares the context by setting the appropriate instance variables. - # :api: plugin def _prepare_context @view_flow = OutputFlow.new @output_buffer = nil @@ -29,7 +28,6 @@ module ActionView # Encapsulates the interaction with the view flow so it # returns the correct buffer on +yield+. This is usually # overwritten by helpers to add more behavior. - # :api: plugin def _layout_for(name = nil) name ||= :layout view_flow.get(name).html_safe diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index 2648f9153f..62b5047f56 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -75,8 +75,7 @@ module ActionView end # Returns an object that is able to render templates. - # :api: private - def view_renderer + def view_renderer # :nodoc: @_view_renderer ||= ActionView::Renderer.new(lookup_context) end @@ -92,7 +91,6 @@ module ActionView private # Find and render a template based on the options given. - # :api: private def _render_template(options) variant = options.delete(:variant) assigns = options.delete(:assigns) @@ -114,7 +112,6 @@ module ActionView # Normalize args by converting render "foo" to render :action => "foo" and # render "foo/bar" to render :template => "foo/bar". - # :api: private def _normalize_args(action = nil, options = {}) options = super(action, options) case action @@ -137,7 +134,6 @@ module ActionView end # Normalize options. - # :api: private def _normalize_options(options) options = super(options) if options[:partial] == true -- cgit v1.2.3 From c83f28fffe8b18ee75822394a11f446c05f692fd Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 30 Sep 2017 20:07:09 +0900 Subject: Ensure `AliasTracker` respects a custom table name --- .../lib/active_record/associations/association_scope.rb | 2 +- .../lib/active_record/associations/join_dependency.rb | 2 +- activerecord/test/cases/relations_test.rb | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index b2dc044312..6118cef913 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -23,7 +23,7 @@ module ActiveRecord reflection = association.reflection scope = klass.unscoped owner = association.owner - alias_tracker = AliasTracker.create(klass.connection, klass.table_name) + alias_tracker = AliasTracker.create(klass.connection, scope.table.name) chain = get_chain(reflection, association, alias_tracker) scope.extending! reflection.extensions diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index 77b3d11b47..23741b2f6a 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -91,7 +91,7 @@ module ActiveRecord # joins # => [] # def initialize(base, table, associations, joins, eager_loading: true) - @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins) + @alias_tracker = AliasTracker.create_with_joins(base.connection, table.name, joins) @eager_loading = eager_loading tree = self.class.make_tree associations @join_root = JoinBase.new(base, table, build(tree, base)) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 4edaf79e9a..e1788be351 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -19,12 +19,12 @@ require "models/tyre" require "models/minivan" require "models/possession" require "models/reader" +require "models/category" require "models/categorization" require "models/edge" class RelationTest < ActiveRecord::TestCase - fixtures :authors, :author_addresses, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments, - :tags, :taggings, :cars, :minivans + fixtures :authors, :author_addresses, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans class TopicWithCallbacks < ActiveRecord::Base self.table_name = :topics @@ -1810,6 +1810,10 @@ class RelationTest < ActiveRecord::TestCase assert_equal [posts(:welcome)], custom_post_relation.ranked_by_comments.limit_by(1).to_a end + test "alias_tracker respects a custom table" do + assert_equal posts(:welcome), custom_post_relation("categories_posts").joins(:categories).first + end + test "#load" do relation = Post.all assert_queries(1) do @@ -1930,8 +1934,8 @@ class RelationTest < ActiveRecord::TestCase end private - def custom_post_relation - table_alias = Post.arel_table.alias("omg_posts") + def custom_post_relation(alias_name = "omg_posts") + table_alias = Post.arel_table.alias(alias_name) table_metadata = ActiveRecord::TableMetadata.new(Post, table_alias) predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata) -- cgit v1.2.3 From 88dc74b78468546748fdfdc4133e153efcc1f1c9 Mon Sep 17 00:00:00 2001 From: Patrick Davey Date: Sun, 1 Oct 2017 08:47:24 +1300 Subject: Fix broken link to recaptcha.net [ci skip] The link to recaptcha.net returns a 404. As far as I can tell, the new link ought to be to https://developers.google.com/recaptcha/ . --- guides/source/security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/security.md b/guides/source/security.md index a07d583f15..bf9af88c5d 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -535,7 +535,7 @@ Depending on your web application, there may be more ways to hijack the user's a INFO: _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect registration forms from attackers and comment forms from automatic spam bots by asking the user to type the letters of a distorted image. This is the positive CAPTCHA, but there is also the negative CAPTCHA. The idea of a negative CAPTCHA is not for a user to prove that they are human, but reveal that a robot is a robot._ -A popular positive CAPTCHA API is [reCAPTCHA](http://recaptcha.net/) which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. [ReCAPTCHA](https://github.com/ambethia/recaptcha/) is also a Rails plug-in with the same name as the API. +A popular positive CAPTCHA API is [reCAPTCHA](https://developers.google.com/recaptcha/) which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. [ReCAPTCHA](https://github.com/ambethia/recaptcha/) is also a Rails plug-in with the same name as the API. You will get two keys from the API, a public and a private key, which you have to put into your Rails environment. After that you can use the recaptcha_tags method in the view, and the verify_recaptcha method in the controller. Verify_recaptcha will return false if the validation fails. The problem with CAPTCHAs is that they have a negative impact on the user experience. Additionally, some visually impaired users have found certain kinds of distorted CAPTCHAs difficult to read. Still, positive CAPTCHAs are one of the best methods to prevent all kinds of bots from submitting forms. -- cgit v1.2.3 From 853abf125a4bdd34cc3a8e2e99e33ac67b0798d6 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 1 Oct 2017 08:55:03 +0900 Subject: Remove unused `new_credentials_configuration` `new_credentials_configuration` is no longer used since 081a6ac6f7fd929798481f9ee333fb92b441356c. --- activesupport/test/encrypted_configuration_test.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/activesupport/test/encrypted_configuration_test.rb b/activesupport/test/encrypted_configuration_test.rb index 53ea9e393f..471faa8c12 100644 --- a/activesupport/test/encrypted_configuration_test.rb +++ b/activesupport/test/encrypted_configuration_test.rb @@ -54,12 +54,4 @@ class EncryptedConfigurationTest < ActiveSupport::TestCase test "raises key error when accessing config via bang method" do assert_raise(KeyError) { @credentials.something! } end - - private - def new_credentials_configuration - ActiveSupport::EncryptedConfiguration.new \ - config_path: @credentials_config_path, - key_path: @credentials_key_path, - env_key: "RAILS_MASTER_KEY" - end end -- cgit v1.2.3 From 2ae89f8b799e11a2b67e24a31952ab11f445f944 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 2 Oct 2017 06:56:57 +0900 Subject: Make internal methods in `AbstractController::Rendering` to private --- actionpack/lib/abstract_controller/rendering.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 4b4915a85d..7e156586b9 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -71,6 +71,7 @@ module AbstractController } end + private # Normalize args by converting render "foo" to # render :action => "foo" and render "foo/bar" to # render :file => "foo/bar". -- cgit v1.2.3 From d1eb0ef88bfded6d797203e09c71d74bd651e09b Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 2 Oct 2017 08:03:24 +0900 Subject: Do not create credentials in dummy application Because dummy application is only for use test, so credentials is unnecessary. --- railties/lib/rails/generators/rails/app/app_generator.rb | 4 ++-- railties/lib/rails/generators/rails/plugin/plugin_generator.rb | 1 + railties/test/generators/plugin_generator_test.rb | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 23fdf03b05..409d0f816c 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -159,7 +159,7 @@ module Rails end def master_key - return if options[:pretend] + return if options[:pretend] || options[:dummy_app] require_relative "../master_key/master_key_generator" @@ -169,7 +169,7 @@ module Rails end def credentials - return if options[:pretend] + return if options[:pretend] || options[:dummy_app] require_relative "../credentials/credentials_generator" diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index eb941adf95..6b40fbb602 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -99,6 +99,7 @@ task default: :test opts[:skip_listen] = true opts[:skip_git] = true opts[:skip_turbolinks] = true + opts[:dummy_app] = true invoke Rails::Generators::AppGenerator, [ File.expand_path(dummy_path, destination_root) ], opts diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 38130ceb68..51ff95190f 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -475,6 +475,8 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_no_file "test/dummy/Gemfile" assert_no_file "test/dummy/public/robots.txt" assert_no_file "test/dummy/README.md" + assert_no_file "test/dummy/config/master.key" + assert_no_file "test/dummy/config/credentials.yml.enc" assert_no_directory "test/dummy/lib/tasks" assert_no_directory "test/dummy/test" assert_no_directory "test/dummy/vendor" -- cgit v1.2.3 From 87598c8c80f4cbeef114267d75c46f1c87ca057a Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 2 Oct 2017 15:46:30 +0900 Subject: Make automatically synchronize test schema work inside engine In Rails engine, migration files are in under `db/migrate` of engine. Therefore, when rake task is executed in engine, `db/migrate` is automatically added to `DatabaseTasks.migrations_paths`. https://github.com/rails/rails/blob/a18cf23a9cbcbeed61e8049442640c7153e0a8fb/activerecord/lib/active_record/railtie.rb#L39..L43 However, if execute the rake task under dummy app, migration files will not be loaded because engine's migration path setting process is not called. Therefore, in order to load migration files correctly, it is necessary to execute rake task under engine. Fixes #30765 --- activerecord/lib/active_record/migration.rb | 3 ++- railties/lib/rails/tasks/engine.rake | 3 +++ railties/test/engine/test_test.rb | 31 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 railties/test/engine/test_test.rb diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 8845e26ab7..ec8119e5da 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -581,7 +581,8 @@ module ActiveRecord def load_schema_if_pending! if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations? # Roundtrip to Rake to allow plugins to hook into database initialization. - FileUtils.cd Rails.root do + root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root + FileUtils.cd(root) do current_config = Base.connection_config Base.clear_all_connections! system("bin/rails db:test:prepare") diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake index 3ce49a1641..c967c54aa5 100644 --- a/railties/lib/rails/tasks/engine.rake +++ b/railties/lib/rails/tasks/engine.rake @@ -70,6 +70,9 @@ namespace :db do desc "Retrieves the current schema version number" app_task "version" + + # desc 'Load the test schema' + app_task "test:prepare" end def find_engine_path(path) diff --git a/railties/test/engine/test_test.rb b/railties/test/engine/test_test.rb new file mode 100644 index 0000000000..18af85a0aa --- /dev/null +++ b/railties/test/engine/test_test.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class Rails::Engine::TestTest < ActiveSupport::TestCase + setup do + @destination_root = Dir.mktmpdir("bukkits") + Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --mountable` } + end + + teardown do + FileUtils.rm_rf(@destination_root) + end + + test "automatically synchronize test schema" do + Dir.chdir(plugin_path) do + # In order to confirm that migration files are loaded, generate multiple migration files. + `bin/rails generate model user name:string; + bin/rails generate model todo name:string; + RAILS_ENV=development bin/rails db:migrate` + + output = `bin/rails test test/models/bukkits/user_test.rb` + assert_includes(output, "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips") + end + end + + private + def plugin_path + "#{@destination_root}/bukkits" + end +end -- cgit v1.2.3 From e42100cf42345e84a83c3e711671b7e6de7fef2b Mon Sep 17 00:00:00 2001 From: Antonio Tapiador del Dujo Date: Mon, 2 Oct 2017 13:40:03 +0200 Subject: Add link to Rack website Resemble links to Bundler and Yarn, based on feedback by @kamipo [ci skip] --- guides/source/getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 70a945ad9e..8965e74fe2 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -172,7 +172,7 @@ of the files and folders that Rails created by default: |app/|Contains the controllers, models, views, helpers, mailers, channels, jobs and assets for your application. You'll focus on this folder for the remainder of this guide.| |bin/|Contains the rails script that starts your app and can contain other scripts you use to setup, update, deploy or run your application.| |config/|Configure your application's routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html).| -|config.ru|Rack configuration for Rack based servers used to start the application.| +|config.ru|Rack configuration for Rack based servers used to start the application. For more information about Rack, see the [Rack website](https://rack.github.io/).| |db/|Contains your current database schema, as well as the database migrations.| |Gemfile
Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see the [Bundler website](https://bundler.io).| |lib/|Extended modules for your application.| -- cgit v1.2.3 From f01f2e648dce5edf833f104775494a1066d4544b Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Mon, 2 Oct 2017 16:45:32 -0500 Subject: rails-ujs: Update README Make various wording tweaks to cater to users who are viewing the README on NPM. Notably, don't highlight Yarn specifically in the installation instructions -- even though this is the preferred tool of choice especially in the Ruby community, some people still use NPM (and, really, ES2015+ syntax has nothing to do with NPM or Yarn). --- actionview/app/assets/javascripts/README.md | 41 +++++++++++++---------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/actionview/app/assets/javascripts/README.md b/actionview/app/assets/javascripts/README.md index f321b9f720..8198011b02 100644 --- a/actionview/app/assets/javascripts/README.md +++ b/actionview/app/assets/javascripts/README.md @@ -1,5 +1,4 @@ -Ruby on Rails unobtrusive scripting adapter. -======================================== +# Ruby on Rails unobtrusive scripting adapter This unobtrusive scripting support file is developed for the Ruby on Rails framework, but is not strictly tied to any specific backend. You can drop this into any application to: @@ -8,51 +7,47 @@ This unobtrusive scripting support file is developed for the Ruby on Rails frame - make forms or hyperlinks submit data asynchronously with Ajax; - have submit buttons become automatically disabled on form submit to prevent double-clicking. -These features are achieved by adding certain ["data" attributes][data] to your HTML markup. In Rails, they are added by the framework's template helpers. +These features are achieved by adding certain [`data` attributes][data] to your HTML markup. In Rails, they are added by the framework's template helpers. -Requirements ------------- +## Optional prerequisites -- HTML5 doctype (optional). +Note that the `data` attributes this library adds are a feature of HTML5. If you're not targeting HTML5, these attributes may make your HTML to fail [validation][validator]. However, this shouldn't create any issues for web browsers or other user agents. -If you don't use HTML5, adding "data" attributes to your HTML4 or XHTML pages might make them fail [W3C markup validation][validator]. However, this shouldn't create any issues for web browsers or other user agents. +## Installation -Installation using npm ------------- +### NPM -Run `npm install rails-ujs --save` to install the rails-ujs package. + npm install rails-ujs --save + +### Yarn + + yarn add rails-ujs -Installation using Yarn ------------- +## Usage -Run `yarn add rails-ujs` to install the rails-ujs package. +### Asset pipeline -Usage ------------- - -Require `rails-ujs` in your application.js manifest. +In a conventional Rails application that uses the asset pipeline, require `rails-ujs` in your `application.js` manifest: ```javascript //= require rails-ujs ``` -Usage with yarn ------------- +### ES2015+ -When using with the Webpacker gem or your preferred JavaScript bundler, just -add the following to your main JS file and compile. +If you're using the Webpacker gem or some other JavaScript bundler, add the following to your main JS file: ```javascript import Rails from 'rails-ujs'; Rails.start() ``` -How to run tests ------------- +## How to run tests Run `bundle exec rake ujs:server` first, and then run the web tests by visiting http://localhost:4567 in your browser. ## License + rails-ujs is released under the [MIT License](MIT-LICENSE). [data]: http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes "Embedding custom non-visible data with the data-* attributes" -- cgit v1.2.3 From b407b5973552e266f3797285721438b304e3a7b7 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Tue, 3 Oct 2017 08:50:11 +0900 Subject: Add test case for `font_url` Since test of `font_url` was not in Action View's test suite, so it added. --- actionview/test/template/asset_tag_helper_test.rb | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index 6645839f0e..182d8f89f7 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -305,6 +305,24 @@ class AssetTagHelperTest < ActionView::TestCase %(font_path("font.ttf?123")) => %(/fonts/font.ttf?123) } + FontUrlToTag = { + %(font_url("font.eot")) => %(http://www.example.com/fonts/font.eot), + %(font_url("font.eot#iefix")) => %(http://www.example.com/fonts/font.eot#iefix), + %(font_url("font.woff")) => %(http://www.example.com/fonts/font.woff), + %(font_url("font.ttf")) => %(http://www.example.com/fonts/font.ttf), + %(font_url("font.ttf?123")) => %(http://www.example.com/fonts/font.ttf?123), + %(font_url("font.ttf", host: "http://assets.example.com")) => %(http://assets.example.com/fonts/font.ttf) + } + + UrlToFontToTag = { + %(url_to_font("font.eot")) => %(http://www.example.com/fonts/font.eot), + %(url_to_font("font.eot#iefix")) => %(http://www.example.com/fonts/font.eot#iefix), + %(url_to_font("font.woff")) => %(http://www.example.com/fonts/font.woff), + %(url_to_font("font.ttf")) => %(http://www.example.com/fonts/font.ttf), + %(url_to_font("font.ttf?123")) => %(http://www.example.com/fonts/font.ttf?123), + %(url_to_font("font.ttf", host: "http://assets.example.com")) => %(http://assets.example.com/fonts/font.ttf) + } + def test_autodiscovery_link_tag_with_unknown_type_but_not_pass_type_option_key assert_raise(ArgumentError) do auto_discovery_link_tag(:xml) @@ -547,6 +565,14 @@ class AssetTagHelperTest < ActionView::TestCase FontPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end + def test_font_url + FontUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } + end + + def test_url_to_font_alias_for_font_url + UrlToFontToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } + end + def test_video_audio_tag_does_not_modify_options options = { autoplay: true } video_tag("video", options) -- cgit v1.2.3 From e2dbfe5adac3438e3f8bbd2e2dce2ad39c91221b Mon Sep 17 00:00:00 2001 From: Erich Soares Machado Date: Tue, 3 Oct 2017 00:49:13 +0200 Subject: Fixes ActiveSupport::Cache::FileStore#cleanup bug which prevented it from cleaning up the expired cache keys --- activesupport/lib/active_support/cache/file_store.rb | 5 ++--- activesupport/test/cache/stores/file_store_test.rb | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 28df2c066e..f98d343ccd 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -39,9 +39,8 @@ module ActiveSupport def cleanup(options = nil) options = merged_options(options) search_dir(cache_path) do |fname| - key = file_path_key(fname) - entry = read_entry(key, options) - delete_entry(key, options) if entry && entry.expired? + entry = read_entry(fname, options) + delete_entry(fname, options) if entry && entry.expired? end end diff --git a/activesupport/test/cache/stores/file_store_test.rb b/activesupport/test/cache/stores/file_store_test.rb index 391ab60b3a..66231b0a82 100644 --- a/activesupport/test/cache/stores/file_store_test.rb +++ b/activesupport/test/cache/stores/file_store_test.rb @@ -118,6 +118,7 @@ class FileStoreTest < ActiveSupport::TestCase assert_not @cache.exist?("foo") assert @cache.exist?("baz") assert @cache.exist?("quux") + assert_equal 2, Dir.glob(File.join(cache_dir, "**")).size end end -- cgit v1.2.3 From 71b15603fddd85602813d1c96857e2674b6075d0 Mon Sep 17 00:00:00 2001 From: Felipe Oliveira Date: Tue, 3 Oct 2017 08:41:12 -0300 Subject: Add update_only example to AR nested attributes doc [ci_skip] --- activerecord/lib/active_record/nested_attributes.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 435c81c153..fa20bce3a9 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -63,6 +63,18 @@ module ActiveRecord # member.update params[:member] # member.avatar.icon # => 'sad' # + # If you want to update the current avatar without providing the id, you must add :update_only option. + # + # class Member < ActiveRecord::Base + # has_one :avatar + # accepts_nested_attributes_for :avatar, update_only: true + # end + # + # params = { member: { avatar_attributes: { icon: 'sad' } } } + # member.update params[:member] + # member.avatar.id # => 2 + # member.avatar.icon # => 'sad' + # # By default you will only be able to set and update attributes on the # associated model. If you want to destroy the associated model through the # attributes hash, you have to enable it first using the -- cgit v1.2.3 From 45ed61ac4731eb91f39d3762889dce0da899af45 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Tue, 3 Oct 2017 08:27:21 -0500 Subject: Associate blobs with their attachments --- activestorage/app/models/active_storage/blob.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 7477b09d09..ff785d4f61 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -23,6 +23,8 @@ class ActiveStorage::Blob < ActiveRecord::Base class_attribute :service + has_many :attachments + has_one_attached :preview_image class << self -- cgit v1.2.3 From 53c16188924c7f4179555cdc6ae6911e44743d60 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Wed, 4 Oct 2017 03:36:21 +0900 Subject: Fix third-party system libraries list in ActiveStorage::Preview [ci skip] --- activestorage/app/models/active_storage/preview.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activestorage/app/models/active_storage/preview.rb b/activestorage/app/models/active_storage/preview.rb index 42c4bbc5a4..be5053edae 100644 --- a/activestorage/app/models/active_storage/preview.rb +++ b/activestorage/app/models/active_storage/preview.rb @@ -23,8 +23,8 @@ # # The built-in previewers rely on third-party system libraries: # -# * {ffmpeg}[https://www.ffmpeg.org] -# * {mupdf}[https://mupdf.com] +# * {ffmpeg}[https://www.ffmpeg.org] +# * {mupdf}[https://mupdf.com] # # These libraries are not provided by Rails. You must install them yourself to use the built-in previewers. Before you # install and use third-party software, make sure you understand the licensing implications of doing so. -- cgit v1.2.3 From 723ff351e105a3546f1112e70b7f6d204717127f Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 4 Oct 2017 06:53:59 +0900 Subject: Prefer official name PostgreSQL over Postgres --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 4d37a292d6..5b6ad5fbc4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -312,14 +312,14 @@ module ActiveRecord def get_advisory_lock(lock_id) # :nodoc: unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63 - raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer") + raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer") end query_value("SELECT pg_try_advisory_lock(#{lock_id})") end def release_advisory_lock(lock_id) # :nodoc: unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63 - raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer") + raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer") end query_value("SELECT pg_advisory_unlock(#{lock_id})") end -- cgit v1.2.3 From fced30c3738542211f761bb1ccdeff8441c4587e Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 4 Oct 2017 07:27:33 +0900 Subject: Cleanup CHANGELOGs [ci skip] * Add missing credit * Add backticks * Fix indentation * Remove trailing spaces And some minor tweaks. --- actionmailer/CHANGELOG.md | 2 +- activerecord/CHANGELOG.md | 22 +++++++++++----------- activesupport/CHANGELOG.md | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index fe60f8a6a1..49afec9a12 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -3,7 +3,7 @@ assert_enqueued_email_with ContactMailer, :welcome do ContactMailer.welcome.deliver_later end - + *Mikkel Malmberg* * Allow Action Mailer classes to configure their delivery job. diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index f73e27b91f..288f22a9d3 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,22 +1,22 @@ -* PostgreSQL `tsrange` now preserves subsecond precision +* PostgreSQL `tsrange` now preserves subsecond precision. PostgreSQL 9.1+ introduced range types, and Rails added support for using - this datatype in ActiveRecord. However, the serialization of - PostgreSQL::OID::Range was incomplete, because it did not properly + this datatype in Active Record. However, the serialization of + `PostgreSQL::OID::Range` was incomplete, because it did not properly cast the bounds that make up the range. This led to subseconds being dropped in SQL commands: - (byebug) from = type_cast_single_for_database(range.first) - 2010-01-01 13:30:00 UTC + Before: - (byebug) to = type_cast_single_for_database(range.last) - 2011-02-02 19:30:00 UTC + connection.type_cast(tsrange.serialize(range_value)) + # => "[2010-01-01 13:30:00 UTC,2011-02-02 19:30:00 UTC)" - (byebug) "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}" - "[2010-01-01 13:30:00 UTC,2011-02-02 19:30:00 UTC)" + Now: - (byebug) "[#{type_cast(from)},#{type_cast(to)}#{value.exclude_end? ? ')' : ']'}" - "['2010-01-01 13:30:00.670277','2011-02-02 19:30:00.745125')" + connection.type_cast(tsrange.serialize(range_value)) + # => "[2010-01-01 13:30:00.670277,2011-02-02 19:30:00.745125)" + + *Thomas Cannon* * Passing a `Set` to `Relation#where` now behaves the same as passing an array. diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index c95f95d076..c7924fa9ae 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -61,12 +61,12 @@ Previously: 'one_two'.camelize(true) - => nil + # => nil Now: 'one_two'.camelize(true) - => ArgumentError: Invalid option, use either :upper or :lower. + # => ArgumentError: Invalid option, use either :upper or :lower. *Ricardo Díaz* @@ -81,12 +81,12 @@ Prior to Rails 5.1: 5.minutes % 2.minutes - => 60 + # => 60 Now: 5.minutes % 2.minutes - => 1 minute + # => 1 minute Fixes #29603 and #29743. -- cgit v1.2.3 From 6f456ae2db5b37b25410333563645789dc6a7ed4 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 4 Oct 2017 18:21:00 +0900 Subject: Fix formatting of `Time.use_zone` [ci skip] --- activesupport/lib/active_support/core_ext/time/zones.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index c48edb135f..0aae073fb5 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -55,10 +55,10 @@ class Time # end # end # - # NOTE: This won't affect any ActiveSupport::TimeWithZone - # objects that have already been created, e.g. any model timestamp - # attributes that have been read before the block will remain in - # the application's default timezone. + # NOTE: This won't affect any ActiveSupport::TimeWithZone + # objects that have already been created, e.g. any model timestamp + # attributes that have been read before the block will remain in + # the application's default timezone. def use_zone(time_zone) new_zone = find_zone!(time_zone) begin -- cgit v1.2.3 From 85ca01bcb319d5ce66c0fa9ce6aede533492f0e9 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 4 Oct 2017 13:56:51 +0300 Subject: Express #change_column_comment as public api Implemented by #22911 Related to #30677 --- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4f0c1890be..e6bcdbda60 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1174,7 +1174,7 @@ module ActiveRecord end # Changes the comment for a column or removes it if +nil+. - def change_column_comment(table_name, column_name, comment) #:nodoc: + def change_column_comment(table_name, column_name, comment) raise NotImplementedError, "#{self.class} does not support changing column comments" end -- cgit v1.2.3 From 59a02fb7bcbe68f26e1e7fdcec45c00c66e4a065 Mon Sep 17 00:00:00 2001 From: eileencodes Date: Tue, 26 Sep 2017 13:27:53 -0400 Subject: Implement H2 Early Hints for Rails When puma/puma#1403 is merged Puma will support the Early Hints status code for sending assets before a request has finished. While the Early Hints spec is still in draft, this PR prepares Rails to allowing this status code. If the proxy server supports Early Hints, it will send H2 pushes to the client. This PR adds a method for setting Early Hints Link headers via Rails, and also automatically sends Early Hints if supported from the `stylesheet_link_tag` and the `javascript_include_tag`. Once puma supports Early Hints the `--early-hints` argument can be passed to the server to enable this or set in the puma config with `early_hints(true)`. Note that for Early Hints to work in the browser the requirements are 1) a proxy that can handle H2, and 2) HTTPS. To start the server with Early Hints enabled pass `--early-hints` to `rails s`. This has been verified to work with h2o, Puma, and Rails with Chrome. The commit adds a new option to the rails server to enable early hints for Puma. Early Hints spec: https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-04 [Eileen M. Uchitelle, Aaron Patterson] --- actionpack/CHANGELOG.md | 8 ++++++ actionpack/lib/action_dispatch/http/request.rb | 17 ++++++++++++ actionpack/test/dispatch/request_test.rb | 15 +++++++++++ .../lib/action_view/helpers/asset_tag_helper.rb | 30 +++++++++++++++++++--- actionview/test/template/asset_tag_helper_test.rb | 5 +++- actionview/test/template/javascript_helper_test.rb | 4 +++ .../lib/rails/commands/server/server_command.rb | 8 +++++- railties/test/commands/server_test.rb | 12 +++++++++ 8 files changed, 93 insertions(+), 6 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 16090e7946..adb86aad9f 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,11 @@ +* Add ability to enable Early Hints for HTTP/2 + + If supported by the server, and enabled in Puma this allows H2 Early Hints to be used. + + The `javascript_include_tag` and the `stylesheet_link_tag` automatically add Early Hints if requested. + + *Eileen M. Uchitelle*, *Aaron Patterson* + * Simplify cookies middleware with key rotation support Use the `rotate` method for both `MessageEncryptor` and diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index dee7be184a..5c172aecad 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -199,6 +199,23 @@ module ActionDispatch @headers ||= Http::Headers.new(self) end + # Early Hints is an HTTP/2 status code that indicates hints to help a client start + # making preparations for processing the final response. + # + # If the env contains +rack.early_hints+ then the server accepts HTTP2 push for Link headers. + # + # The +send_early_hints+ method accepts an hash of links as follows: + # + # send_early_hints("Link" => "; rel=preload; as=style\n; rel=preload") + # + # If you are using +javascript_include_tag+ or +stylesheet_link_tag+ the + # Early Hints headers are included by default if supported. + def send_early_hints(links) + return unless env["rack.early_hints"] + + env["rack.early_hints"].call(links) + end + # Returns a +String+ with the last requested path including their params. # # # get '/foo' diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 68c6d26364..2a18395aac 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -1304,3 +1304,18 @@ class RequestFormData < BaseRequestTest assert !request.form_data? end end + +class EarlyHintsRequestTest < BaseRequestTest + def setup + super + @env["rack.early_hints"] = lambda { |links| links } + @request = stub_request + end + + test "when early hints is set in the env link headers are sent" do + early_hints = @request.send_early_hints("Link" => "; rel=preload; as=style\n; rel=preload") + expected_hints = { "Link" => "; rel=preload; as=style\n; rel=preload" } + + assert_equal expected_hints, early_hints + end +end diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index bc2713d13e..f4f1030ac4 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -37,6 +37,9 @@ 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. # + # If the server supports Early Hints header links for these assets will be + # automatically pushed. + # # ==== Options # # When the last parameter is a hash you can add HTML attributes using that @@ -77,12 +80,20 @@ module ActionView def javascript_include_tag(*sources) options = sources.extract_options!.stringify_keys path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys - sources.uniq.map { |source| + early_hints_links = [] + + sources_tags = sources.uniq.map { |source| + href = path_to_javascript(source, path_options) + early_hints_links << "<#{href}>; rel=preload; as=script" tag_options = { - "src" => path_to_javascript(source, path_options) + "src" => href }.merge!(options) content_tag("script".freeze, "", tag_options) }.join("\n").html_safe + + request.send_early_hints("Link" => early_hints_links.join("\n")) + + sources_tags end # Returns a stylesheet link tag for the sources specified as arguments. If @@ -92,6 +103,9 @@ module ActionView # to "screen", so you must explicitly set it to "all" for the stylesheet(s) to # apply to all media types. # + # If the server supports Early Hints header links for these assets will be + # automatically pushed. + # # stylesheet_link_tag "style" # # => # @@ -113,14 +127,22 @@ module ActionView def stylesheet_link_tag(*sources) options = sources.extract_options!.stringify_keys path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys - sources.uniq.map { |source| + early_hints_links = [] + + sources_tags = sources.uniq.map { |source| + href = path_to_stylesheet(source, path_options) + early_hints_links << "<#{href}>; rel=preload; as=stylesheet" tag_options = { "rel" => "stylesheet", "media" => "screen", - "href" => path_to_stylesheet(source, path_options) + "href" => href }.merge!(options) tag(:link, tag_options) }.join("\n").html_safe + + request.send_early_hints("Link" => early_hints_links.join("\n")) + + sources_tags end # Returns a link tag that browsers and feed readers can use to auto-detect diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index 182d8f89f7..7475f5cc3c 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -19,6 +19,7 @@ class AssetTagHelperTest < ActionView::TestCase def ssl?() false end def host_with_port() "localhost" end def base_url() "http://www.example.com" end + def send_early_hints(links) end end.new @controller.request = @request @@ -653,7 +654,9 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase @controller = BasicController.new @controller.config.relative_url_root = "/collaboration/hieraki" - @request = Struct.new(:protocol, :base_url).new("gopher://", "gopher://www.example.com") + @request = Struct.new(:protocol, :base_url) do + def send_early_hints(links); end + end.new("gopher://", "gopher://www.example.com") @controller.request = @request end diff --git a/actionview/test/template/javascript_helper_test.rb b/actionview/test/template/javascript_helper_test.rb index 4478c9f4ab..a72bc6c2fe 100644 --- a/actionview/test/template/javascript_helper_test.rb +++ b/actionview/test/template/javascript_helper_test.rb @@ -6,11 +6,15 @@ class JavaScriptHelperTest < ActionView::TestCase tests ActionView::Helpers::JavaScriptHelper attr_accessor :output_buffer + attr_reader :request setup do @old_escape_html_entities_in_json = ActiveSupport.escape_html_entities_in_json ActiveSupport.escape_html_entities_in_json = true @template = self + @request = Class.new do + def send_early_hints(links) end + end.new end def teardown diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index 785265d766..5b5037d3de 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -127,6 +127,7 @@ module Rails class_option "dev-caching", aliases: "-C", type: :boolean, default: nil, desc: "Specifies whether to perform caching in development." class_option "restart", type: :boolean, default: nil, hide: true + class_option "early_hints", type: :boolean, default: nil, desc: "Enables HTTP/2 early hints." def initialize(args = [], local_options = {}, config = {}) @original_options = local_options @@ -161,7 +162,8 @@ module Rails daemonize: options[:daemon], pid: pid, caching: options["dev-caching"], - restart_cmd: restart_command + restart_cmd: restart_command, + early_hints: early_hints } end end @@ -227,6 +229,10 @@ module Rails "bin/rails server #{@server} #{@original_options.join(" ")} --restart" end + def early_hints + options[:early_hints] + end + def pid File.expand_path(options[:pid]) end diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 556c2289e7..a6201e4f04 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -81,6 +81,18 @@ class Rails::ServerTest < ActiveSupport::TestCase assert_equal false, options[:caching] end + def test_early_hints_with_option + args = ["--early-hints"] + options = parse_arguments(args) + assert_equal true, options[:early_hints] + end + + def test_early_hints_is_nil_by_default + args = [] + options = parse_arguments(args) + assert_nil options[:early_hints] + end + def test_log_stdout with_rack_env nil do with_rails_env nil do -- cgit v1.2.3 From 8a8ac422d8c388bf5f39ae2e6ec3ae84780186e7 Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 5 Oct 2017 13:21:03 +0900 Subject: Use __callee__ to pass alias instead of original method name Before ``` > Article.left_joins ArgumentError: The method .left_outer_joins() must contain arguments. ``` After ``` > Article.left_joins ArgumentError: The method .left_joins() must contain arguments. ``` --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index c88603fde2..e76ad05b71 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -441,7 +441,7 @@ module ActiveRecord # => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id" # def left_outer_joins(*args) - check_if_method_has_arguments!(:left_outer_joins, args) + check_if_method_has_arguments!(__callee__, args) args.compact! args.flatten! -- cgit v1.2.3 From 214dc7d82d8a87914d5d426b7e8460300000acc2 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 2 Oct 2017 08:29:00 +0900 Subject: Extract repeatedly appeared prepending compatible table definition --- .../lib/active_record/migration/compatibility.rb | 49 +++++++++++----------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index 502cef2e20..2b247f7a7a 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -52,11 +52,8 @@ module ActiveRecord end if block_given? - super(table_name, options) do |t| - class << t - prepend TableDefinition - end - yield t + super do |t| + yield compatible_table_definition(t) end else super @@ -65,11 +62,8 @@ module ActiveRecord def change_table(table_name, options = {}) if block_given? - super(table_name, options) do |t| - class << t - prepend TableDefinition - end - yield t + super do |t| + yield compatible_table_definition(t) end else super @@ -80,11 +74,8 @@ module ActiveRecord column_options.reverse_merge!(type: :integer) if block_given? - super(table_1, table_2, column_options: column_options, **options) do |t| - class << t - prepend TableDefinition - end - yield t + super do |t| + yield compatible_table_definition(t) end else super @@ -103,6 +94,14 @@ module ActiveRecord super(table_name, ref_name, type: :integer, **options) end alias :add_belongs_to :add_reference + + private + def compatible_table_definition(t) + class << t + prepend TableDefinition + end + t + end end class V4_2 < V5_0 @@ -121,11 +120,8 @@ module ActiveRecord def create_table(table_name, options = {}) if block_given? - super(table_name, options) do |t| - class << t - prepend TableDefinition - end - yield t + super do |t| + yield compatible_table_definition(t) end else super @@ -134,11 +130,8 @@ module ActiveRecord def change_table(table_name, options = {}) if block_given? - super(table_name, options) do |t| - class << t - prepend TableDefinition - end - yield t + super do |t| + yield compatible_table_definition(t) end else super @@ -174,6 +167,12 @@ module ActiveRecord end private + def compatible_table_definition(t) + class << t + prepend TableDefinition + end + t + end def index_name_for_remove(table_name, options = {}) index_name = index_name(table_name, options) -- cgit v1.2.3 From 563c16c7cbccc2f3a088139bfb855865a51b027e Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 2 Jun 2017 04:43:08 +0900 Subject: Add JSON attribute test cases for SQLite3 adapter --- .../connection_adapters/abstract_adapter.rb | 2 ++ .../connection_adapters/abstract_mysql_adapter.rb | 1 - .../test/cases/adapters/sqlite3/json_test.rb | 29 ++++++++++++++++++++++ activerecord/test/cases/json_shared_test_cases.rb | 2 ++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 activerecord/test/cases/adapters/sqlite3/json_test.rb diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 8c889f98f5..6859feb2f3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -478,6 +478,8 @@ module ActiveRecord m.alias_type %r(number)i, "decimal" m.alias_type %r(double)i, "float" + m.register_type %r(^json)i, Type::Json.new + m.register_type(%r(decimal)i) do |sql_type| scale = extract_scale(sql_type) precision = extract_precision(sql_type) 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 ae991d3d79..add6f8632b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -563,7 +563,6 @@ module ActiveRecord m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1) m.register_type %r(^float)i, Type::Float.new(limit: 24) m.register_type %r(^double)i, Type::Float.new(limit: 53) - m.register_type %r(^json)i, Type::Json.new register_integer_type m, %r(^bigint)i, limit: 8 register_integer_type m, %r(^int)i, limit: 4 diff --git a/activerecord/test/cases/adapters/sqlite3/json_test.rb b/activerecord/test/cases/adapters/sqlite3/json_test.rb new file mode 100644 index 0000000000..568a524058 --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite3/json_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "cases/helper" +require "cases/json_shared_test_cases" + +class SQLite3JSONTest < ActiveRecord::SQLite3TestCase + include JSONSharedTestCases + + def setup + super + @connection.create_table("json_data_type") do |t| + t.column "payload", :json, default: {} + t.column "settings", :json + end + end + + def test_default + @connection.add_column "json_data_type", "permissions", column_type, default: { "users": "read", "posts": ["read", "write"] } + klass.reset_column_information + + assert_equal({ "users" => "read", "posts" => ["read", "write"] }, klass.column_defaults["permissions"]) + assert_equal({ "users" => "read", "posts" => ["read", "write"] }, klass.new.permissions) + end + + private + def column_type + :json + end +end diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb index 56ec8c8a82..a71485982c 100644 --- a/activerecord/test/cases/json_shared_test_cases.rb +++ b/activerecord/test/cases/json_shared_test_cases.rb @@ -30,6 +30,7 @@ module JSONSharedTestCases end def test_change_table_supports_json + skip unless @connection.supports_json? @connection.change_table("json_data_type") do |t| t.public_send column_type, "users" end @@ -40,6 +41,7 @@ module JSONSharedTestCases end def test_schema_dumping + skip unless @connection.supports_json? output = dump_table_schema("json_data_type") assert_match(/t\.#{column_type}\s+"settings"/, output) end -- cgit v1.2.3 From 9cbebd70eae7aa78a5b165dd3cae8e53c9a8ab83 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 6 Oct 2017 03:37:27 +0900 Subject: Move duplicated code to `delete_or_destroy` in `CollectionAssociation` --- .../lib/active_record/associations/collection_association.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index ceedf150e3..ed215fb22c 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -181,8 +181,6 @@ module ActiveRecord # are actually removed from the database, that depends precisely on # +delete_records+. They are in any case removed from the collection. def delete(*records) - return if records.empty? - records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) } delete_or_destroy(records, options[:dependent]) end @@ -192,8 +190,6 @@ module ActiveRecord # Note that this method removes records from the database ignoring the # +:dependent+ option. def destroy(*records) - return if records.empty? - records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) } delete_or_destroy(records, :destroy) end @@ -376,6 +372,8 @@ module ActiveRecord end def delete_or_destroy(records, method) + return if records.empty? + records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) } records = records.flatten records.each { |record| raise_on_type_mismatch!(record) } existing_records = records.reject(&:new_record?) -- cgit v1.2.3 From d1170ddd41e5e88c94dd1c4d7dde2c4212d265c1 Mon Sep 17 00:00:00 2001 From: pavel Date: Thu, 5 Oct 2017 23:34:58 +0200 Subject: request check --- actionview/lib/action_view/helpers/asset_tag_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index f4f1030ac4..10b366b030 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -91,7 +91,7 @@ module ActionView content_tag("script".freeze, "", tag_options) }.join("\n").html_safe - request.send_early_hints("Link" => early_hints_links.join("\n")) + request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) sources_tags end @@ -140,7 +140,7 @@ module ActionView tag(:link, tag_options) }.join("\n").html_safe - request.send_early_hints("Link" => early_hints_links.join("\n")) + request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) sources_tags end -- cgit v1.2.3 From 0bf8d1e4a607611ff7eaeb88a5c73804d6b4d8f3 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 6 Oct 2017 09:27:20 +0900 Subject: Remove unused `secrets.yml` template `secrets.yml` no longer used since #30067. Together, removed `app_secret` method that only used in `secrets.yml` --- .../rails/generators/rails/app/app_generator.rb | 4 --- .../rails/app/templates/config/secrets.yml | 32 ---------------------- 2 files changed, 36 deletions(-) delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/secrets.yml diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 23fdf03b05..b431495781 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -509,10 +509,6 @@ module Rails end end - def app_secret - SecureRandom.hex(64) - end - def mysql_socket @mysql_socket ||= [ "/tmp/mysql.sock", # default diff --git a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml deleted file mode 100644 index ea9d47396c..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Your secret key is used for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! - -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -# You can use `rails secret` to generate a secure secret key. - -# Make sure the secrets in this file are kept private -# if you're sharing your code publicly. - -# Shared secrets are available across all environments. - -# shared: -# api_key: a1B2c3D4e5F6 - -# Environmental secrets are only available for that specific environment. - -development: - secret_key_base: <%= app_secret %> - -test: - secret_key_base: <%= app_secret %> - -# Do not keep production secrets in the unencrypted secrets file. -# Instead, either read values from the environment. -# Or, use `bin/rails secrets:setup` to configure encrypted secrets -# and move the `production:` environment over there. - -production: - secret_key_base: <%%= ENV["SECRET_KEY_BASE"] %> -- cgit v1.2.3 From ead60686e810df4b49bf19f4f113b48f16ae560f Mon Sep 17 00:00:00 2001 From: khall Date: Wed, 4 Oct 2017 12:26:04 -0700 Subject: Replace variation key use with SHA256 of key to prevent long filenames If a variant has a large set of options associated with it, the generated filename will be too long, causing Errno::ENAMETOOLONG to be raised. This change replaces those potentially long filenames with a much more compact SHA256 hash. Fixes #30662. --- activestorage/app/models/active_storage/variant.rb | 2 +- activestorage/test/models/variant_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb index 54685b4c0e..90a3605331 100644 --- a/activestorage/app/models/active_storage/variant.rb +++ b/activestorage/app/models/active_storage/variant.rb @@ -50,7 +50,7 @@ class ActiveStorage::Variant # Returns a combination key of the blob and the variation that together identifies a specific variant. def key - "variants/#{blob.key}/#{variation.key}" + "variants/#{blob.key}/#{Digest::SHA256.hexdigest(variation.key)}" end # Returns the URL of the variant on the service. This URL is intended to be short-lived for security and not used directly diff --git a/activestorage/test/models/variant_test.rb b/activestorage/test/models/variant_test.rb index d7cbef4e55..b7d20ab55a 100644 --- a/activestorage/test/models/variant_test.rb +++ b/activestorage/test/models/variant_test.rb @@ -26,4 +26,9 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase assert_equal 67, image.height assert_match(/Gray/, image.colorspace) end + + test "service_url doesn't grow in length despite long variant options" do + variant = @blob.variant(font: "a" * 10_000).processed + assert_operator variant.service_url.length, :<, 500 + end end -- cgit v1.2.3 From de2afdc457d63ee628b5cac4e7bb73812e016e38 Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Fri, 6 Oct 2017 09:02:56 +0300 Subject: Cosmetic fixes [ci skip] --- guides/source/2_3_release_notes.md | 4 ++-- guides/source/3_2_release_notes.md | 2 +- guides/source/4_0_release_notes.md | 2 +- guides/source/4_2_release_notes.md | 2 +- guides/source/5_0_release_notes.md | 2 +- guides/source/action_cable_overview.md | 2 +- guides/source/action_controller_overview.md | 2 +- guides/source/action_view_overview.md | 2 +- guides/source/active_model_basics.md | 2 +- guides/source/asset_pipeline.md | 12 ++++++------ guides/source/command_line.md | 2 +- guides/source/configuring.md | 4 ++-- guides/source/engines.md | 2 +- guides/source/getting_started.md | 2 +- guides/source/plugins.md | 2 +- guides/source/upgrading_ruby_on_rails.md | 6 +++--- 16 files changed, 25 insertions(+), 25 deletions(-) diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md index 3f5a3c7ade..1020f4a8e7 100644 --- a/guides/source/2_3_release_notes.md +++ b/guides/source/2_3_release_notes.md @@ -231,7 +231,7 @@ Rails chooses between file, template, and action depending on whether there is a ### Application Controller Renamed -If you're one of the people who has always been bothered by the special-case naming of `application.rb`, rejoice! It's been reworked to be application_controller.rb in Rails 2.3. In addition, there's a new rake task, `rake rails:update:application_controller` to do this automatically for you - and it will be run as part of the normal `rake rails:update` process. +If you're one of the people who has always been bothered by the special-case naming of `application.rb`, rejoice! It's been reworked to be `application_controller.rb` in Rails 2.3. In addition, there's a new rake task, `rake rails:update:application_controller` to do this automatically for you - and it will be run as part of the normal `rake rails:update` process. * More Information: * [The Death of Application.rb](http://afreshcup.com/2008/11/17/rails-2x-the-death-of-applicationrb/) @@ -304,7 +304,7 @@ Rails now keeps a per-request local cache of read from the remote cache stores, Rails can now provide localized views, depending on the locale that you have set. For example, suppose you have a `Posts` controller with a `show` action. By default, this will render `app/views/posts/show.html.erb`. But if you set `I18n.locale = :da`, it will render `app/views/posts/show.da.html.erb`. If the localized template isn't present, the undecorated version will be used. Rails also includes `I18n#available_locales` and `I18n::SimpleBackend#available_locales`, which return an array of the translations that are available in the current Rails project. -In addition, you can use the same scheme to localize the rescue files in the `public` directory: `public/500.da.html` or `public/404.en.html` work, for example. +In addition, you can use the same scheme to localize the rescue files in the public directory: `public/500.da.html` or `public/404.en.html` work, for example. ### Partial Scoping for Translations diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index 6570b19f97..f6571544f9 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -30,7 +30,7 @@ TIP: Note that Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails. ### What to update in your apps -* Update your Gemfile to depend on +* Update your `Gemfile` to depend on * `rails = 3.2.0` * `sass-rails ~> 3.2.3` * `coffee-rails ~> 3.2.1` diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index 6f1b75a42b..0921cd1979 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -66,7 +66,7 @@ Major Features * **ActiveRecord session store** ([commit](https://github.com/rails/rails/commit/0ffe19056c8e8b2f9ae9d487b896cad2ce9387ad)) - The ActiveRecord session store is extracted to a separate gem. Storing sessions in SQL is costly. Instead, use cookie sessions, memcache sessions, or a custom session store. * **ActiveModel mass assignment protection** ([commit](https://github.com/rails/rails/commit/f8c9a4d3e88181cee644f91e1342bfe896ca64c6)) - Rails 3 mass assignment protection is deprecated. Instead, use strong parameters. * **ActiveResource** ([commit](https://github.com/rails/rails/commit/f1637bf2bb00490203503fbd943b73406e043d1d)) - ActiveResource is extracted to a separate gem. ActiveResource was not widely used. -* **vendor/plugins removed** ([commit](https://github.com/rails/rails/commit/853de2bd9ac572735fa6cf59fcf827e485a231c3)) - Use a Gemfile to manage installed gems. +* **vendor/plugins removed** ([commit](https://github.com/rails/rails/commit/853de2bd9ac572735fa6cf59fcf827e485a231c3)) - Use a `Gemfile` to manage installed gems. ### ActionPack diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index a30bfc458a..036a310ac8 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -179,7 +179,7 @@ change your code to use the explicit form (`render file: "foo/bar"`) instead. `respond_with` and the corresponding class-level `respond_to` have been moved to the [responders](https://github.com/plataformatec/responders) gem. Add -`gem 'responders', '~> 2.0'` to your Gemfile to use it: +`gem 'responders', '~> 2.0'` to your `Gemfile` to use it: ```ruby # app/controllers/users_controller.rb diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md index 3805fd2a63..656838c6b8 100644 --- a/guides/source/5_0_release_notes.md +++ b/guides/source/5_0_release_notes.md @@ -775,7 +775,7 @@ Please refer to the [Changelog][active-record] for detailed changes. * Added prepared statements support to `mysql2` adapter, for mysql2 0.4.4+, Previously this was only supported on the deprecated `mysql` legacy adapter. - To enable, set `prepared_statements: true` in config/database.yml. + To enable, set `prepared_statements: true` in `config/database.yml`. ([Pull Request](https://github.com/rails/rails/pull/23461)) * Added ability to call `ActionRecord::Relation#update` on relation objects diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md index 31151e0329..dd16ba3932 100644 --- a/guides/source/action_cable_overview.md +++ b/guides/source/action_cable_overview.md @@ -557,7 +557,7 @@ The async adapter is intended for development/testing and should not be used in Action Cable contains two Redis adapters: "normal" Redis and Evented Redis. Both of the adapters require users to provide a URL pointing to the Redis server. -Additionally, a channel_prefix may be provided to avoid channel name collisions +Additionally, a `channel_prefix` may be provided to avoid channel name collisions when using the same Redis server for multiple applications. See the [Redis PubSub documentation](https://redis.io/topics/pubsub#database-amp-scoping) for more details. ##### PostgreSQL Adapter diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 5fb8e300de..014559f36b 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -1116,7 +1116,7 @@ Rails default exception handling displays a "500 Server Error" message for all e ### The Default 500 and 404 Templates -By default a production application will render either a 404 or a 500 error message, in the development environment all unhandled exceptions are raised. These messages are contained in static HTML files in the `public` folder, in `404.html` and `500.html` respectively. You can customize these files to add some extra information and style, but remember that they are static HTML; i.e. you can't use ERB, SCSS, CoffeeScript, or layouts for them. +By default a production application will render either a 404 or a 500 error message, in the development environment all unhandled exceptions are raised. These messages are contained in static HTML files in the public folder, in `404.html` and `500.html` respectively. You can customize these files to add some extra information and style, but remember that they are static HTML; i.e. you can't use ERB, SCSS, CoffeeScript, or layouts for them. ### `rescue_from` diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index a57623428f..349108c207 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -490,7 +490,7 @@ stylesheet_link_tag "application" # => /assets/application.css diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md index b8f076a27b..ee0472621b 100644 --- a/guides/source/active_model_basics.md +++ b/guides/source/active_model_basics.md @@ -464,7 +464,7 @@ a `password` accessor with certain validations on it. #### Requirements `ActiveModel::SecurePassword` depends on [`bcrypt`](https://github.com/codahale/bcrypt-ruby 'BCrypt'), -so include this gem in your Gemfile to use `ActiveModel::SecurePassword` correctly. +so include this gem in your `Gemfile` to use `ActiveModel::SecurePassword` correctly. In order to make this work, the model must have an accessor named `password_digest`. The `has_secure_password` will add the following validations on the `password` accessor: diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 8bd1f91304..805b0f0d62 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -65,7 +65,7 @@ config.assets.js_compressor = :uglifier ``` NOTE: The `sass-rails` gem is automatically used for CSS compression if included -in the Gemfile and no `config.assets.css_compressor` option is set. +in the `Gemfile` and no `config.assets.css_compressor` option is set. ### Main Features @@ -181,7 +181,7 @@ When you generate a scaffold or a controller, Rails also generates a JavaScript file (or CoffeeScript file if the `coffee-rails` gem is in the `Gemfile`) and a Cascading Style Sheet file (or SCSS file if `sass-rails` is in the `Gemfile`) for that controller. Additionally, when generating a scaffold, Rails generates -the file scaffolds.css (or scaffolds.scss if `sass-rails` is in the +the file `scaffolds.css` (or `scaffolds.scss` if `sass-rails` is in the `Gemfile`.) For example, if you generate a `ProjectsController`, Rails will also add a new @@ -202,7 +202,7 @@ will result in your assets being included more than once. WARNING: When using asset precompilation, you will need to ensure that your controller assets will be precompiled when loading them on a per page basis. By -default .coffee and .scss files will not be precompiled on their own. See +default `.coffee` and `.scss` files will not be precompiled on their own. See [Precompiling Assets](#precompiling-assets) for more information on how precompiling works. @@ -726,7 +726,7 @@ include, you can add them to the `precompile` array in `config/initializers/asse Rails.application.config.assets.precompile += %w( admin.js admin.css ) ``` -NOTE. Always specify an expected compiled filename that ends with .js or .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 `.sprockets-manifest-md5hash.json` (where `md5hash` is @@ -1090,7 +1090,7 @@ Possible options for JavaScript compression are `:closure`, `:uglifier` and `:yui`. These require the use of the `closure-compiler`, `uglifier` or `yui-compressor` gems, respectively. -The default Gemfile includes [uglifier](https://github.com/lautis/uglifier). +The default `Gemfile` includes [uglifier](https://github.com/lautis/uglifier). This gem wraps [UglifyJS](https://github.com/mishoo/UglifyJS) (written for NodeJS) in Ruby. It compresses your code by removing white space and comments, shortening local variable names, and performing other micro-optimizations such @@ -1219,7 +1219,7 @@ Sprockets uses Processors, Transformers, Compressors, and Exporters to extend Sprockets functionality. Have a look at [Extending Sprockets](https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md) to learn more. Here we registered a preprocessor to add a comment to the end -of text/css (.css) files. +of text/css (`.css`) files. ```ruby module AddComment diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 2cd8e02a77..88c559921c 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -659,6 +659,6 @@ development: ... ``` -It also generated some lines in our database.yml configuration corresponding to our choice of PostgreSQL for database. +It also generated some lines in our `database.yml` configuration corresponding to our choice of PostgreSQL for database. NOTE. The only catch with using the SCM options is that you have to make your application's directory first, then initialize your SCM, then you can run the `rails new` command to generate the basis of your app. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 0f87d73d6e..05bb9a96c2 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -391,7 +391,7 @@ by setting up a Rake task which runs ``` for all models and all boolean columns, after which the flag must be set to true -by adding the following to your application.rb file: +by adding the following to your `application.rb` file: ```ruby Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true @@ -572,7 +572,7 @@ Defaults to `'signed cookie'`. error should be raised for missing translations. * `config.action_view.automatically_disable_submit_tag` determines whether - submit_tag should automatically disable on click, this defaults to `true`. + `submit_tag` should automatically disable on click, this defaults to `true`. * `config.action_view.debug_missing_translation` determines whether to wrap the missing translations key in a `` tag or not. This defaults to `true`. diff --git a/guides/source/engines.md b/guides/source/engines.md index 188620a683..738de5d588 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -653,7 +653,7 @@ there isn't an application handy to test this out in, generate one using the $ rails new unicorn ``` -Usually, specifying the engine inside the Gemfile would be done by specifying it +Usually, specifying the engine inside the `Gemfile` would be done by specifying it as a normal, everyday gem. ```ruby diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 8965e74fe2..a1ae515055 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -179,7 +179,7 @@ of the files and folders that Rails created by default: |log/|Application log files.| |package.json|This file allows you to specify what npm dependencies are needed for your Rails application. This file is used by Yarn. For more information about Yarn, see the [Yarn website](https://yarnpkg.com/lang/en/).| |public/|The only folder seen by the world as-is. Contains static files and compiled assets.| -|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the `lib/tasks` directory of your application.| +|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing `Rakefile`, you should add your own tasks by adding files to the `lib/tasks` directory of your application.| |README.md|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.| |test/|Unit tests, fixtures, and other test apparatus. These are covered in [Testing Rails Applications](testing.html).| |tmp/|Temporary files (like cache and pid files).| diff --git a/guides/source/plugins.md b/guides/source/plugins.md index 5048444cb2..15073af6be 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -446,7 +446,7 @@ Publishing Your Gem ------------------- Gem plugins currently in development can easily be shared from any Git repository. To share the Yaffle gem with others, simply -commit the code to a Git repository (like GitHub) and add a line to the Gemfile of the application in question: +commit the code to a Git repository (like GitHub) and add a line to the `Gemfile` of the application in question: ```ruby gem "yaffle", git: "https://github.com/rails/yaffle.git" diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index d932fc8d8f..9bc87e4bf0 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -27,7 +27,7 @@ The process should go as follows: 3. Fix tests and deprecated features. 4. Move to the latest patch version of the next minor version. -Repeat this process until you reach your target Rails version. Each time you move versions, you will need to change the Rails version number in the Gemfile (and possibly other gem versions) and run `bundle update`. Then run the Update task mentioned below to update configuration files, then run your tests. +Repeat this process until you reach your target Rails version. Each time you move versions, you will need to change the Rails version number in the `Gemfile` (and possibly other gem versions) and run `bundle update`. Then run the Update task mentioned below to update configuration files, then run your tests. You can find a list of all released Rails versions [here](https://rubygems.org/gems/rails/versions). @@ -411,7 +411,7 @@ Upgrading from Rails 4.1 to Rails 4.2 ### Web Console -First, add `gem 'web-console', '~> 2.0'` to the `:development` group in your Gemfile and run `bundle install` (it won't have been included when you upgraded Rails). Once it's been installed, you can simply drop a reference to the console helper (i.e., `<%= console %>`) into any view you want to enable it for. A console will also be provided on any error page you view in your development environment. +First, add `gem 'web-console', '~> 2.0'` to the `:development` group in your `Gemfile` and run `bundle install` (it won't have been included when you upgraded Rails). Once it's been installed, you can simply drop a reference to the console helper (i.e., `<%= console %>`) into any view you want to enable it for. A console will also be provided on any error page you view in your development environment. ### Responders @@ -1136,7 +1136,7 @@ full support for the last few changes in the specification. ### Gemfile Rails 4.0 removed the `assets` group from Gemfile. You'd need to remove that -line from your Gemfile when upgrading. You should also update your application +line from your `Gemfile` when upgrading. You should also update your application file (in `config/application.rb`): ```ruby -- cgit v1.2.3 From 2ab561932e7ba76f4b39e9e32e1163567a0dc3fe Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 6 Oct 2017 12:50:05 -0700 Subject: how do we install active_storage? [skip ci] --- activestorage/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activestorage/README.md b/activestorage/README.md index 8814887950..78e4463c5a 100644 --- a/activestorage/README.md +++ b/activestorage/README.md @@ -12,6 +12,10 @@ A key difference to how Active Storage works compared to other attachment soluti `Blob` models store attachment metadata (filename, content-type, etc.), and their identifier key in the storage service. Blob models do not store the actual binary data. They are intended to be immutable in spirit. One file, one blob. You can associate the same blob with multiple application models as well. And if you want to do transformations of a given `Blob`, the idea is that you'll simply create a new one, rather than attempt to mutate the existing one (though of course you can delete the previous version later if you don't need it). +## Installation + +Run `rails active_storage:install` to copy over active_storage migrations. + ## Examples One attachment: -- cgit v1.2.3 From 62f6d4ed39081a218e1bb6953d4a41421e86c02b Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sat, 7 Oct 2017 11:20:53 +0900 Subject: Add v5.1 to earlier releases list in guide [ci skip] --- guides/source/_welcome.html.erb | 1 + 1 file changed, 1 insertion(+) diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index 8afec00018..6959f992aa 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -16,6 +16,7 @@ <% end %>

The guides for earlier releases: +Rails 5.1, Rails 5.0, Rails 4.2, Rails 4.1, -- cgit v1.2.3 From d342a35eaa59717155da0a646f9a44435f15c052 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 7 Oct 2017 11:52:22 +0900 Subject: Remove needless setup from `InfoTest` The `abstract_unit` loads `rails/all`. Therefore, `Rails` is definitely defined. Also, since `Info` is autoloaded, do not need to explicitly load the file. https://github.com/rails/rails/blob/3ede539357acc91e377611cea9dd5f30678e7b2a/railties/lib/rails.rb#L29 --- railties/test/rails_info_test.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb index 227a739b71..43b60b9144 100644 --- a/railties/test/rails_info_test.rb +++ b/railties/test/rails_info_test.rb @@ -2,20 +2,7 @@ require "abstract_unit" -unless defined?(Rails) && defined?(Rails::Info) - module Rails - class Info; end - end -end - -require "active_support/core_ext/kernel/reporting" - class InfoTest < ActiveSupport::TestCase - def setup - Rails.send :remove_const, :Info - silence_warnings { load "rails/info.rb" } - end - def test_property_with_block_swallows_exceptions_and_ignores_property assert_nothing_raised do Rails::Info.module_eval do -- cgit v1.2.3 From ce9cd1b67dce243c0ca030bf2fef350dac44a46f Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sat, 7 Oct 2017 12:20:58 +0900 Subject: Update Instrumentation guide for Active Storage [ci skip] --- guides/source/active_support_instrumentation.md | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index ff4288a7f5..893b18ce0d 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -450,6 +450,53 @@ Active Job | `:adapter` | QueueAdapter object processing the job | | `:job` | Job object | +Active Storage +-------------- + +### service_upload.active_storage + +| Key | Value | +| ------------ | ---------------------------- | +| `:key` | Secure token | +| `:service` | Name of the service | +| `:checksum` | Checksum to ensure integrity | + +### service_streaming_download.active_storage + +| Key | Value | +| ------------ | ------------------- | +| `:key` | Secure token | +| `:service` | Name of the service | + +### service_download.active_storage + +| Key | Value | +| ------------ | ------------------- | +| `:key` | Secure token | +| `:service` | Name of the service | + +### service_delete.active_storage + +| Key | Value | +| ------------ | ------------------- | +| `:key` | Secure token | +| `:service` | Name of the service | + +### service_exist.active_storage + +| Key | Value | +| ------------ | --------------------------- | +| `:key` | Secure token | +| `:service` | Name of the service | +| `:exist` | File or blob exists or not | + +### service_url.active_storage + +| Key | Value | +| ------------ | ------------------- | +| `:key` | Secure token | +| `:service` | Name of the service | +| `:url` | Generated url | Railties -------- -- cgit v1.2.3 From 823c17e4ab6cad89ebb711e6989327be170867e0 Mon Sep 17 00:00:00 2001 From: Akshay Vishnoi Date: Sat, 7 Oct 2017 03:08:51 +0530 Subject: [ci skip] Fix typo --- guides/source/active_support_instrumentation.md | 2 +- guides/source/configuring.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index ff4288a7f5..b32dda190c 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -549,4 +549,4 @@ end ``` You should follow Rails conventions when defining your own events. The format is: `event.library`. -If you application is sending Tweets, you should create an event named `tweet.twitter`. +If your application is sending Tweets, you should create an event named `tweet.twitter`. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 05bb9a96c2..7a32607eb7 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -1317,7 +1317,7 @@ know which pages it is allowed to index. Rails creates this file for you inside the `/public` folder. By default, it allows search engines to index all pages of your application. If you want to block -indexing on all pages of you application, use this: +indexing on all pages of your application, use this: ``` User-agent: * -- cgit v1.2.3 From 11a89674aff171155043fa284035243982842d8d Mon Sep 17 00:00:00 2001 From: Masato Oba Date: Sun, 8 Oct 2017 10:25:45 +0900 Subject: Fix radio_button_tag comment Colons, periods, etc. can also be included in id. The sanitize_to_id method does not remove them. --- actionview/lib/action_view/helpers/form_tag_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 31a1f8be8c..8a08e49e2f 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -394,7 +394,7 @@ module ActionView # # => # # radio_button_tag 'time_slot', "3:00 p.m.", false, disabled: true - # # => + # # => # # radio_button_tag 'color', "green", true, class: "color_input" # # => -- cgit v1.2.3 From c4bbce969779179457218558be7554b4ee4b3230 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 8 Oct 2017 13:09:30 +0900 Subject: Simplify parse arguments in `ConsoleTest` If need a parse result of arguments, can obtain it by creating an instance of the command. --- railties/test/commands/console_test.rb | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb index 7eb26c355c..45ab8d87ff 100644 --- a/railties/test/commands/console_test.rb +++ b/railties/test/commands/console_test.rb @@ -172,21 +172,8 @@ class Rails::ConsoleTest < ActiveSupport::TestCase end def parse_arguments(args) - Rails::Command::ConsoleCommand.class_eval do - alias_method :old_perform, :perform - define_method(:perform) do - extract_environment_option_from_argument - - options - end - end - - Rails::Command.invoke(:console, args) - ensure - Rails::Command::ConsoleCommand.class_eval do - undef_method :perform - alias_method :perform, :old_perform - undef_method :old_perform - end + command = Rails::Command::ConsoleCommand.new([], args) + command.send(:extract_environment_option_from_argument) + command.options end end -- cgit v1.2.3 From 2ee33e6c56289642f61b99d125555559cda36f95 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 8 Oct 2017 18:34:43 +0900 Subject: Fix default of `String#to_time` [ci skip] The default of `String#to_time` is `:local` since b79adc4. --- guides/source/active_support_core_extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index ae573cc77c..067a7b7cb6 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -1752,7 +1752,7 @@ The methods `to_date`, `to_time`, and `to_datetime` are basically convenience wr "2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200 ``` -Default is `:utc`. +Default is `:local`. Please refer to the documentation of `Date._parse` for further details. -- cgit v1.2.3 From d20f4b73bd65a4c679aa31fb95795148d7799ae7 Mon Sep 17 00:00:00 2001 From: Chris Salzberg Date: Sun, 8 Oct 2017 21:25:12 +0900 Subject: Include Mutex_m into module class instead of extending instance --- activerecord/lib/active_record/attribute_methods.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index e4ca6c8408..891e556dc4 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -33,7 +33,9 @@ module ActiveRecord BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass) - class GeneratedAttributeMethods < Module; end # :nodoc: + class GeneratedAttributeMethods < Module #:nodoc: + include Mutex_m + end module ClassMethods def inherited(child_class) #:nodoc: @@ -42,7 +44,7 @@ module ActiveRecord end def initialize_generated_modules # :nodoc: - @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m } + @generated_attribute_methods = GeneratedAttributeMethods.new @attribute_methods_generated = false include @generated_attribute_methods -- cgit v1.2.3 From 8ad8bbaef8c4f521f292765a02ddb91e48160366 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 7 Oct 2017 17:29:45 +0900 Subject: Decouple building `AliasTracker` from `JoinDependency` This is preparation to respect parent relation's alias tracking for fixing #30681. --- .../lib/active_record/associations/alias_tracker.rb | 14 ++++---------- .../lib/active_record/associations/association_scope.rb | 3 +-- .../lib/active_record/associations/join_dependency.rb | 4 ++-- activerecord/lib/active_record/relation.rb | 4 ++++ activerecord/lib/active_record/relation/finder_methods.rb | 4 +++- activerecord/lib/active_record/relation/merger.rb | 2 +- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index 096f016976..358cdabc7f 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -6,22 +6,16 @@ module ActiveRecord module Associations # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency class AliasTracker # :nodoc: - def self.create(connection, initial_table) - aliases = Hash.new(0) - aliases[initial_table] = 1 - new(connection, aliases) - end - - def self.create_with_joins(connection, initial_table, joins) + def self.create(connection, initial_table, joins) if joins.empty? - create(connection, initial_table) + aliases = Hash.new(0) else aliases = Hash.new { |h, k| h[k] = initial_count_for(connection, k, joins) } - aliases[initial_table] = 1 - new(connection, aliases) end + aliases[initial_table] = 1 + new(connection, aliases) end def self.initial_count_for(connection, name, table_joins) diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index 6118cef913..11967e0571 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -23,8 +23,7 @@ module ActiveRecord reflection = association.reflection scope = klass.unscoped owner = association.owner - alias_tracker = AliasTracker.create(klass.connection, scope.table.name) - chain = get_chain(reflection, association, alias_tracker) + chain = get_chain(reflection, association, scope.alias_tracker) scope.extending! reflection.extensions add_constraints(scope, owner, chain) diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index 23741b2f6a..b70b680885 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -90,8 +90,8 @@ module ActiveRecord # associations # => [:appointments] # joins # => [] # - def initialize(base, table, associations, joins, eager_loading: true) - @alias_tracker = AliasTracker.create_with_joins(base.connection, table.name, joins) + def initialize(base, table, associations, alias_tracker, eager_loading: true) + @alias_tracker = alias_tracker @eager_loading = eager_loading tree = self.class.make_tree associations @join_root = JoinBase.new(base, table, build(tree, base)) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3517091a6e..1f59a0c53a 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -551,6 +551,10 @@ module ActiveRecord limit_value || offset_value end + def alias_tracker(joins = []) # :nodoc: + ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins) + end + protected def load_records(records) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index c92d5a52f4..5d4b549470 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -390,7 +390,9 @@ module ActiveRecord def construct_join_dependency(joins = [], eager_loading: true) including = eager_load_values + includes_values - ActiveRecord::Associations::JoinDependency.new(klass, table, including, joins, eager_loading: eager_loading) + ActiveRecord::Associations::JoinDependency.new( + klass, table, including, alias_tracker(joins), eager_loading: eager_loading + ) end def construct_relation_for_association_calculations diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index 03824ffff9..ebc72d28fd 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -122,7 +122,7 @@ module ActiveRecord end join_dependency = ActiveRecord::Associations::JoinDependency.new( - other.klass, other.table, joins_dependency, [] + other.klass, other.table, joins_dependency, other.alias_tracker ) relation.joins! rest diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e76ad05b71..fb5beeffef 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1013,7 +1013,7 @@ module ActiveRecord join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins) join_dependency = ActiveRecord::Associations::JoinDependency.new( - klass, table, association_joins, join_list + klass, table, association_joins, alias_tracker(join_list) ) joins = join_dependency.join_constraints(stashed_association_joins, join_type) -- cgit v1.2.3 From 54b18a2149537afd516262fd8c4d4ad5e6f4e6d2 Mon Sep 17 00:00:00 2001 From: yalab Date: Fri, 6 Oct 2017 15:25:28 +0900 Subject: Exception message for SystemTestCase#get etc.. --- .../system_testing/test_helpers/undef_methods.rb | 2 +- .../test/dispatch/system_testing/system_test_case_test.rb | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb index ef680cafed..d64be3b3d9 100644 --- a/actionpack/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb @@ -14,7 +14,7 @@ module ActionDispatch def method_missing(method, *args, &block) if METHODS.include?(method) - raise NoMethodError + raise NoMethodError, "System tests cannot make direct requests via ##{method}; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information." else super end diff --git a/actionpack/test/dispatch/system_testing/system_test_case_test.rb b/actionpack/test/dispatch/system_testing/system_test_case_test.rb index b60ec559ae..771a2a8d6e 100644 --- a/actionpack/test/dispatch/system_testing/system_test_case_test.rb +++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb @@ -36,32 +36,37 @@ end class UndefMethodsTest < DrivenBySeleniumWithChrome test "get" do - assert_raise NoMethodError do + exception = assert_raise NoMethodError do get "http://example.com" end + assert_equal "System tests cannot make direct requests via #get; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message end test "post" do - assert_raise NoMethodError do + exception = assert_raise NoMethodError do post "http://example.com" end + assert_equal "System tests cannot make direct requests via #post; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message end test "put" do - assert_raise NoMethodError do + exception = assert_raise NoMethodError do put "http://example.com" end + assert_equal "System tests cannot make direct requests via #put; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message end test "patch" do - assert_raise NoMethodError do + exception = assert_raise NoMethodError do patch "http://example.com" end + assert_equal "System tests cannot make direct requests via #patch; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message end test "delete" do - assert_raise NoMethodError do + exception = assert_raise NoMethodError do delete "http://example.com" end + assert_equal "System tests cannot make direct requests via #delete; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message end end -- cgit v1.2.3 From ff67743fb2d2b2993a395d91086ea0006c685c31 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Thu, 14 Sep 2017 11:43:56 +0300 Subject: Remove redundant execution of `Dir.chdir(app_path) { }` in railties' tests --- railties/test/application/dbconsole_test.rb | 52 +++-- railties/test/application/rake/dbs_test.rb | 107 +++++----- railties/test/application/rake/migrations_test.rb | 249 ++++++++++------------ railties/test/application/rake_test.rb | 16 +- 4 files changed, 194 insertions(+), 230 deletions(-) diff --git a/railties/test/application/dbconsole_test.rb b/railties/test/application/dbconsole_test.rb index ba04c81b4d..8eb293c179 100644 --- a/railties/test/application/dbconsole_test.rb +++ b/railties/test/application/dbconsole_test.rb @@ -19,21 +19,19 @@ module ApplicationTests end def test_use_value_defined_in_environment_file_in_database_yml - Dir.chdir(app_path) do - app_file "config/database.yml", <<-YAML - development: - database: <%= Rails.application.config.database %> - adapter: sqlite3 - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 - YAML + app_file "config/database.yml", <<-YAML + development: + database: <%= Rails.application.config.database %> + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + YAML - app_file "config/environments/development.rb", <<-RUBY - Rails.application.configure do - config.database = "db/development.sqlite3" - end - RUBY - end + app_file "config/environments/development.rb", <<-RUBY + Rails.application.configure do + config.database = "db/development.sqlite3" + end + RUBY master, slave = PTY.open spawn_dbconsole(slave) @@ -43,22 +41,20 @@ module ApplicationTests end def test_respect_environment_option - Dir.chdir(app_path) do - app_file "config/database.yml", <<-YAML - default: &default - adapter: sqlite3 - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 + app_file "config/database.yml", <<-YAML + default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 - development: - <<: *default - database: db/development.sqlite3 + development: + <<: *default + database: db/development.sqlite3 - production: - <<: *default - database: db/production.sqlite3 - YAML - end + production: + <<: *default + database: db/production.sqlite3 + YAML master, slave = PTY.open spawn_dbconsole(slave, "-e production") diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 391676aa31..b7b6ed8a0e 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -83,10 +83,8 @@ module ApplicationTests end test "db:drop failure because database does not exist" do - Dir.chdir(app_path) do - output = rails("db:drop:_unsafe", "--trace") - assert_match(/does not exist/, output) - end + output = rails("db:drop:_unsafe", "--trace") + assert_match(/does not exist/, output) end test "db:drop failure because bad permissions" do @@ -100,13 +98,11 @@ module ApplicationTests end def db_migrate_and_status(expected_database) - Dir.chdir(app_path) do - rails "generate", "model", "book", "title:string" - rails "db:migrate" - output = rails("db:migrate:status") - assert_match(%r{database:\s+\S*#{Regexp.escape(expected_database)}}, output) - assert_match(/up\s+\d{14}\s+Create books/, output) - end + rails "generate", "model", "book", "title:string" + rails "db:migrate" + output = rails("db:migrate:status") + assert_match(%r{database:\s+\S*#{Regexp.escape(expected_database)}}, output) + assert_match(/up\s+\d{14}\s+Create books/, output) end test "db:migrate and db:migrate:status without database_url" do @@ -161,12 +157,11 @@ module ApplicationTests test "db:fixtures:load with namespaced fixture" do require "#{app_path}/config/environment" - Dir.chdir(app_path) do - rails "generate", "model", "admin::book", "title:string" - rails "db:migrate", "db:fixtures:load" - require "#{app_path}/app/models/admin/book" - assert_equal 2, Admin::Book.count - end + + rails "generate", "model", "admin::book", "title:string" + rails "db:migrate", "db:fixtures:load" + require "#{app_path}/app/models/admin/book" + assert_equal 2, Admin::Book.count end def db_structure_dump_and_load(expected_database) @@ -205,56 +200,52 @@ module ApplicationTests end test "db:schema:load and db:structure:load do not purge the existing database" do - Dir.chdir(app_path) do - rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" + rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" - app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 20140423102712) do - create_table(:comments) {} - end - RUBY + app_file "db/schema.rb", <<-RUBY + ActiveRecord::Schema.define(version: 20140423102712) do + create_table(:comments) {} + end + RUBY - list_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip } + list_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip } - assert_equal '["posts"]', list_tables[] - rails "db:schema:load" - assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata"]', list_tables[] + assert_equal '["posts"]', list_tables[] + rails "db:schema:load" + assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata"]', list_tables[] - app_file "db/structure.sql", <<-SQL - CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255)); - SQL + app_file "db/structure.sql", <<-SQL + CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255)); + SQL - rails "db:structure:load" - assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata", "users"]', list_tables[] - end + rails "db:structure:load" + assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata", "users"]', list_tables[] end test "db:schema:load with inflections" do - Dir.chdir(app_path) do - app_file "config/initializers/inflection.rb", <<-RUBY - ActiveSupport::Inflector.inflections do |inflect| - inflect.irregular 'goose', 'geese' - end - RUBY - app_file "config/initializers/primary_key_table_name.rb", <<-RUBY - ActiveRecord::Base.primary_key_prefix_type = :table_name - RUBY - app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 20140423102712) do - create_table("goose".pluralize) do |t| - t.string :name - end + app_file "config/initializers/inflection.rb", <<-RUBY + ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular 'goose', 'geese' + end + RUBY + app_file "config/initializers/primary_key_table_name.rb", <<-RUBY + ActiveRecord::Base.primary_key_prefix_type = :table_name + RUBY + app_file "db/schema.rb", <<-RUBY + ActiveRecord::Schema.define(version: 20140423102712) do + create_table("goose".pluralize) do |t| + t.string :name end - RUBY + end + RUBY - rails "db:schema:load" + rails "db:schema:load" - tables = rails("runner", "p ActiveRecord::Base.connection.tables").strip - assert_match(/"geese"/, tables) + tables = rails("runner", "p ActiveRecord::Base.connection.tables").strip + assert_match(/"geese"/, tables) - columns = rails("runner", "p ActiveRecord::Base.connection.columns('geese').map(&:name)").strip - assert_equal columns, '["gooseid", "name"]' - end + columns = rails("runner", "p ActiveRecord::Base.connection.columns('geese').map(&:name)").strip + assert_equal columns, '["gooseid", "name"]' end test "db:schema:load fails if schema.rb doesn't exist yet" do @@ -300,10 +291,8 @@ module ApplicationTests puts ActiveRecord::Base.connection_config[:database] RUBY - Dir.chdir(app_path) do - database_path = rails("db:setup") - assert_equal "development.sqlite3", File.basename(database_path.strip) - end + database_path = rails("db:setup") + assert_equal "development.sqlite3", File.basename(database_path.strip) ensure ENV["RAILS_ENV"] = @old_rails_env ENV["RACK_ENV"] = @old_rack_env diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index b0d51eb22e..33f2b7038c 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -15,69 +15,63 @@ module ApplicationTests end test "running migrations with given scope" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "model", "user", "username:string", "password:string" - app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION - class AMigration < ActiveRecord::Migration::Current - end - MIGRATION + app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION + class AMigration < ActiveRecord::Migration::Current + end + MIGRATION - output = rails("db:migrate", "SCOPE=bukkits") - assert_no_match(/create_table\(:users\)/, output) - assert_no_match(/CreateUsers/, output) - assert_no_match(/add_column\(:users, :email, :string\)/, output) + output = rails("db:migrate", "SCOPE=bukkits") + assert_no_match(/create_table\(:users\)/, output) + assert_no_match(/CreateUsers/, output) + assert_no_match(/add_column\(:users, :email, :string\)/, output) - assert_match(/AMigration: migrated/, output) + assert_match(/AMigration: migrated/, output) - output = rails("db:migrate", "SCOPE=bukkits", "VERSION=0") - assert_no_match(/drop_table\(:users\)/, output) - assert_no_match(/CreateUsers/, output) - assert_no_match(/remove_column\(:users, :email\)/, output) + output = rails("db:migrate", "SCOPE=bukkits", "VERSION=0") + assert_no_match(/drop_table\(:users\)/, output) + assert_no_match(/CreateUsers/, output) + assert_no_match(/remove_column\(:users, :email\)/, output) - assert_match(/AMigration: reverted/, output) - end + assert_match(/AMigration: reverted/, output) end test "migration with empty version" do - Dir.chdir(app_path) do - output = rails("db:migrate", "VERSION=", allow_failure: true) - assert_match(/Empty VERSION provided/, output) + output = rails("db:migrate", "VERSION=", allow_failure: true) + assert_match(/Empty VERSION provided/, output) - output = rails("db:migrate:redo", "VERSION=", allow_failure: true) - assert_match(/Empty VERSION provided/, output) + output = rails("db:migrate:redo", "VERSION=", allow_failure: true) + assert_match(/Empty VERSION provided/, output) - output = rails("db:migrate:up", "VERSION=", allow_failure: true) - assert_match(/VERSION is required/, output) + output = rails("db:migrate:up", "VERSION=", allow_failure: true) + assert_match(/VERSION is required/, output) - output = rails("db:migrate:up", allow_failure: true) - assert_match(/VERSION is required/, output) + output = rails("db:migrate:up", allow_failure: true) + assert_match(/VERSION is required/, output) - output = rails("db:migrate:down", "VERSION=", allow_failure: true) - assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + output = rails("db:migrate:down", "VERSION=", allow_failure: true) + assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) - output = rails("db:migrate:down", allow_failure: true) - assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) - end + output = rails("db:migrate:down", allow_failure: true) + assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) end test "model and migration generator with change syntax" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - - output = rails("db:migrate") - assert_match(/create_table\(:users\)/, output) - assert_match(/CreateUsers: migrated/, output) - assert_match(/add_column\(:users, :email, :string\)/, output) - assert_match(/AddEmailToUsers: migrated/, output) - - output = rails("db:rollback", "STEP=2") - assert_match(/drop_table\(:users\)/, output) - assert_match(/CreateUsers: reverted/, output) - assert_match(/remove_column\(:users, :email, :string\)/, output) - assert_match(/AddEmailToUsers: reverted/, output) - end + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + + output = rails("db:migrate") + assert_match(/create_table\(:users\)/, output) + assert_match(/CreateUsers: migrated/, output) + assert_match(/add_column\(:users, :email, :string\)/, output) + assert_match(/AddEmailToUsers: migrated/, output) + + output = rails("db:rollback", "STEP=2") + assert_match(/drop_table\(:users\)/, output) + assert_match(/CreateUsers: reverted/, output) + assert_match(/remove_column\(:users, :email, :string\)/, output) + assert_match(/AddEmailToUsers: reverted/, output) end test "migration status when schema migrations table is not present" do @@ -86,93 +80,85 @@ module ApplicationTests end test "migration status" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - rails "db:rollback", "STEP=1" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=1" + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) - end + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) end test "migration status without timestamps" do add_to_config("config.active_record.timestamped_migrations = false") - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) - rails "db:rollback", "STEP=1" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=1" + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/down\s+\d{3,}\s+Add email to users/, output) - end + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/down\s+\d{3,}\s+Add email to users/, output) end test "migration status after rollback and redo" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - rails "db:rollback", "STEP=2" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") - assert_match(/down\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) - rails "db:migrate:redo" - output = rails("db:migrate:status") + rails "db:migrate:redo" + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) - end + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) end test "migration status after rollback and forward" do - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - rails "db:rollback", "STEP=2" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") - assert_match(/down\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) - rails "db:forward", "STEP=2" - output = rails("db:migrate:status") + rails "db:forward", "STEP=2" + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) - end + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) end test "raise error on any move when current migration does not exist" do @@ -209,50 +195,45 @@ module ApplicationTests test "migration status after rollback and redo without timestamps" do add_to_config("config.active_record.timestamped_migrations = false") - Dir.chdir(app_path) do - rails "generate", "model", "user", "username:string", "password:string" - rails "generate", "migration", "add_email_to_users", "email:string" - rails "db:migrate" + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = rails("db:migrate:status") + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) - rails "db:rollback", "STEP=2" - output = rails("db:migrate:status") + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") - assert_match(/down\s+\d{3,}\s+Create users/, output) - assert_match(/down\s+\d{3,}\s+Add email to users/, output) + assert_match(/down\s+\d{3,}\s+Create users/, output) + assert_match(/down\s+\d{3,}\s+Add email to users/, output) - rails "db:migrate:redo" - output = rails("db:migrate:status") + rails "db:migrate:redo" + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) - end + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) end test "running migrations with not timestamp head migration files" do - Dir.chdir(app_path) do + app_file "db/migrate/1_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION - app_file "db/migrate/1_one_migration.rb", <<-MIGRATION - class OneMigration < ActiveRecord::Migration::Current - end - MIGRATION + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION - app_file "db/migrate/02_two_migration.rb", <<-MIGRATION - class TwoMigration < ActiveRecord::Migration::Current - end - MIGRATION + rails "db:migrate" - rails "db:migrate" + output = rails("db:migrate:status") - output = rails("db:migrate:status") - - assert_match(/up\s+001\s+One migration/, output) - assert_match(/up\s+002\s+Two migration/, output) - end + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) end test "schema generation when dump_schema_after_migration is set" do diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 76bc0ce1d7..918566073d 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -40,7 +40,7 @@ module ApplicationTests with_rails_env "test" do rails "generate", "model", "product", "name:string" rails "db:create", "db:migrate" - output = Dir.chdir(app_path) { rails("db:test:prepare", "test") } + output = rails("db:test:prepare", "test") refute_match(/ActiveRecord::ProtectedEnvironmentError/, output) end @@ -372,14 +372,12 @@ module ApplicationTests end def test_copy_templates - Dir.chdir(app_path) do - rails "app:templates:copy" - %w(controller mailer scaffold).each do |dir| - assert File.exist?(File.join(app_path, "lib", "templates", "erb", dir)) - end - %w(controller helper scaffold_controller assets).each do |dir| - assert File.exist?(File.join(app_path, "lib", "templates", "rails", dir)) - end + rails "app:templates:copy" + %w(controller mailer scaffold).each do |dir| + assert File.exist?(File.join(app_path, "lib", "templates", "erb", dir)) + end + %w(controller helper scaffold_controller assets).each do |dir| + assert File.exist?(File.join(app_path, "lib", "templates", "rails", dir)) end end -- cgit v1.2.3 From 0eb2d6079ad3deead60e52a9cdfb5668af201a95 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Thu, 14 Sep 2017 12:28:57 +0300 Subject: Invoke rails command inside the railties' test app with TestHelpers::Generation#rails See #30520 --- railties/test/application/loading_test.rb | 4 ++-- railties/test/application/rake_test.rb | 22 +++++++++------------- railties/test/application/server_test.rb | 2 +- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb index 11c886e991..de1e240fd3 100644 --- a/railties/test/application/loading_test.rb +++ b/railties/test/application/loading_test.rb @@ -300,7 +300,7 @@ class LoadingTest < ActiveSupport::TestCase end MIGRATION - Dir.chdir(app_path) { `rake db:migrate` } + rails("db:migrate") require "#{rails_root}/config/environment" get "/title" @@ -314,7 +314,7 @@ class LoadingTest < ActiveSupport::TestCase end MIGRATION - Dir.chdir(app_path) { `rake db:migrate` } + rails("db:migrate") get "/body" assert_equal "BODY", last_response.body diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 918566073d..002774c2e5 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -101,6 +101,7 @@ module ApplicationTests add_to_config <<-RUBY rake_tasks do task do_nothing: :environment do + puts 'There is nothing' end end RUBY @@ -113,10 +114,8 @@ module ApplicationTests raise 'should not be pre-required for rake even eager_load=true' RUBY - Dir.chdir(app_path) do - assert system("bin/rails do_nothing RAILS_ENV=production"), - "should not be pre-required for rake even eager_load=true" - end + output = rails("do_nothing", "RAILS_ENV=production") + assert_match "There is nothing", output end def test_code_statistics_sanity @@ -294,9 +293,8 @@ module ApplicationTests def test_scaffold_tests_pass_by_default rails "generate", "scaffold", "user", "username:string", "password:string" - output = Dir.chdir(app_path) do - `RAILS_ENV=test bin/rails db:migrate test` - end + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) @@ -313,9 +311,8 @@ module ApplicationTests RUBY rails "generate", "scaffold", "user", "username:string", "password:string" - output = Dir.chdir(app_path) do - `RAILS_ENV=test bin/rails db:migrate test` - end + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) @@ -325,9 +322,8 @@ module ApplicationTests rails "generate", "model", "Product" rails "generate", "model", "Cart" rails "generate", "scaffold", "LineItems", "product:references", "cart:belongs_to" - output = Dir.chdir(app_path) do - `RAILS_ENV=test bin/rails db:migrate test` - end + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb index 6db9a3b9e8..2238f4f63a 100644 --- a/railties/test/application/server_test.rb +++ b/railties/test/application/server_test.rb @@ -42,7 +42,7 @@ module ApplicationTests pid = Process.spawn("#{app_path}/bin/rails server -P tmp/dummy.pid", in: slave, out: slave, err: slave) assert_output("Listening", master) - Dir.chdir(app_path) { system("bin/rails restart") } + rails("restart") assert_output("Restarting", master) assert_output("Inherited", master) -- cgit v1.2.3 From 2329207bec7642bc4b95c82b2bff997d5242b6eb Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Thu, 14 Sep 2017 12:42:49 +0300 Subject: Improve RakeTest#test_db_test_prepare_when_using_sql_format - Remove redundant setting `RAILS_ENV` for `db:test:prepare`. `db:test:prepare` doesn't require it. --- railties/test/application/rake_test.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 002774c2e5..e74100ec93 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -333,9 +333,7 @@ module ApplicationTests add_to_config "config.active_record.schema_format = :sql" rails "generate", "scaffold", "user", "username:string" rails "db:migrate" - output = with_rails_env("test") do - rails "db:test:prepare", "--trace" - end + output = rails("db:test:prepare", "--trace") assert_match(/Execute db:test:load_structure/, output) end -- cgit v1.2.3 From 55a2c101b2889710e1591c9adc15e4d5ca7fb126 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Fri, 29 Sep 2017 18:24:24 -0700 Subject: Distinguish missing adapter gems from load errors within the adapter * When the adapter is missing, raise an exception that points out config typos and missing Gemfile entries. (We can assume that a non-builtin adapter was used since these are always available.) * When loading an adapter raises a LoadError, prefix its error message to indicate that the adapter is likely missing an optional dependency. --- actioncable/lib/action_cable/server/configuration.rb | 19 ++++++++++++++++--- .../connection_adapters/connection_specification.rb | 18 +++++++++++++++--- .../cases/connection_specification/resolver_test.rb | 2 +- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/actioncable/lib/action_cable/server/configuration.rb b/actioncable/lib/action_cable/server/configuration.rb index 82fed81a18..26209537df 100644 --- a/actioncable/lib/action_cable/server/configuration.rb +++ b/actioncable/lib/action_cable/server/configuration.rb @@ -25,13 +25,26 @@ module ActionCable # Also makes sure proper dependencies are required. def pubsub_adapter adapter = (cable.fetch("adapter") { "redis" }) + + # Require the adapter itself and give useful feedback about + # 1. Missing adapter gems and + # 2. Adapter gems' missing dependencies. path_to_adapter = "action_cable/subscription_adapter/#{adapter}" begin require path_to_adapter - rescue Gem::LoadError => e - raise Gem::LoadError, "Specified '#{adapter}' for Action Cable pubsub adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by Action Cable)." rescue LoadError => e - raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/cable.yml is valid. If you use an adapter other than 'postgresql' or 'redis' add the necessary adapter gem to the Gemfile.", e.backtrace + # We couldn't require the adapter itself. Raise an exception that + # points out config typos and missing gems. + if e.path == path_to_adapter + # We can assume that a non-builtin adapter was specified, so it's + # either misspelled or missing from Gemfile. + raise e.class, "Could not load the '#{adapter}' Action Cable pubsub adapter. Ensure that the adapter is spelled correctly in config/cable.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace + + # Bubbled up from the adapter require. Prefix the exception message + # with some guidance about how to address it and reraise. + else + raise e.class, "Error loading the '#{adapter}' Action Cable pubsub adapter. Missing a gem it depends on? #{e.message}", e.backtrace + end end adapter = adapter.camelize diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 29542f917e..e9bda1885b 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -183,13 +183,25 @@ module ActiveRecord raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter) + # Require the adapter itself and give useful feedback about + # 1. Missing adapter gems and + # 2. Adapter gems' missing dependencies. path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter" begin require path_to_adapter - rescue Gem::LoadError => e - raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)." rescue LoadError => e - raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace + # We couldn't require the adapter itself. Raise an exception that + # points out config typos and missing gems. + if e.path == path_to_adapter + # We can assume that a non-builtin adapter was specified, so it's + # either misspelled or missing from Gemfile. + raise e.class, "Could not load the '#{spec[:adapter]}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace + + # Bubbled up from the adapter require. Prefix the exception message + # with some guidance about how to address it and reraise. + else + raise e.class, "Error loading the '#{spec[:adapter]}' Action Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace + end end adapter_method = "#{spec[:adapter]}_connection" diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb index 3fa0ca8366..5b80f16a44 100644 --- a/activerecord/test/cases/connection_specification/resolver_test.rb +++ b/activerecord/test/cases/connection_specification/resolver_test.rb @@ -19,7 +19,7 @@ module ActiveRecord spec "ridiculous://foo?encoding=utf8" end - assert_match "Could not load 'active_record/connection_adapters/ridiculous_adapter'", error.message + assert_match "Could not load the 'ridiculous' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", error.message end # The abstract adapter is used simply to bypass the bit of code that -- cgit v1.2.3 From fb800feeea8b9cba38c8f616b8c7814e090b2905 Mon Sep 17 00:00:00 2001 From: Chris Salzberg Date: Mon, 9 Oct 2017 07:10:00 +0900 Subject: Add test for class of GeneratedAttributeMethods instance in ancestors --- activerecord/test/cases/attribute_methods_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 2d67c57cfb..2f42684212 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -999,6 +999,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal ["title"], model.accessed_fields end + test "generated attribute methods ancestors have correct class" do + mod = Topic.send(:generated_attribute_methods) + assert_match %r(GeneratedAttributeMethods), mod.inspect + end + private def new_topic_like_ar_class(&block) -- cgit v1.2.3 From 53c516d88d48bafee5bd125a368352dd94f5fdad Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 28 Sep 2017 21:54:00 -0700 Subject: redis-rb 4.0 support * Use `gem 'redis', '~> 4.0'` for new app Gemfiles * Loosen Action Cable redis-rb dep to `>= 3.3, < 5` * Bump redis-namespace for looser Redis version dep * Avoid using the underlying `redis.client` directly * Use `Redis.new` instead of `Redis.connect` --- Gemfile | 9 ++-- Gemfile.lock | 49 +++++++++++++++------- actioncable/CHANGELOG.md | 4 ++ .../subscription_adapter/evented_redis.rb | 2 +- .../lib/action_cable/subscription_adapter/redis.rb | 4 +- activejob/CHANGELOG.md | 4 ++ .../test/support/integration/adapters/resque.rb | 11 ++--- railties/CHANGELOG.md | 4 ++ railties/lib/rails/generators/app_base.rb | 2 +- 9 files changed, 59 insertions(+), 30 deletions(-) diff --git a/Gemfile b/Gemfile index 96207e022f..aaf55f18c9 100644 --- a/Gemfile +++ b/Gemfile @@ -61,8 +61,8 @@ gem "bootsnap", ">= 1.1.0", require: false # Active Job. group :job do gem "resque", require: false - gem "resque-scheduler", require: false - gem "sidekiq", require: false + gem "resque-scheduler", github: "jeremy/resque-scheduler", branch: "redis-rb-4.0", require: false + gem "sidekiq", github: "mperham/sidekiq", require: false gem "sucker_punch", require: false gem "delayed_job", require: false gem "queue_classic", github: "QueueClassic/queue_classic", branch: "master", require: false, platforms: :ruby @@ -82,7 +82,10 @@ group :cable do gem "em-hiredis", require: false gem "hiredis", require: false - gem "redis", require: false + gem "redis", "~> 4.0", require: false + + # For Redis 4.0 support. Unreleased 9cb81bf. + gem "redis-namespace", github: "resque/redis-namespace" gem "websocket-client-simple", github: "matthewd/websocket-client-simple", branch: "close-race", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 938b4a71cc..5ed069c13c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,6 +6,17 @@ GIT queue_classic (3.2.0.RC1) pg (>= 0.17, < 0.20) +GIT + remote: https://github.com/jeremy/resque-scheduler.git + revision: 5efd21bb7ee4b326d9c0d26fccad10fc59dd51f0 + branch: redis-rb-4.0 + specs: + resque-scheduler (4.3.0) + mono_logger (~> 1.0) + redis (>= 3.3, < 5) + resque (~> 1.26) + rufus-scheduler (~> 3.2) + GIT remote: https://github.com/matthewd/rb-inotify.git revision: 856730aad4b285969e8dd621e44808a7c5af4242 @@ -23,6 +34,16 @@ GIT event_emitter websocket +GIT + remote: https://github.com/mperham/sidekiq.git + revision: 6332b9f8a316cf1000246701e40e108d16fed6d4 + specs: + sidekiq (5.0.5) + concurrent-ruby (~> 1.0) + connection_pool (~> 2.2, >= 2.2.0) + rack-protection (>= 1.5.0) + redis (>= 3.3.4, < 5) + GIT remote: https://github.com/rails/arel.git revision: 42510bf71472e2e35d9becb546edd05562672344 @@ -41,6 +62,13 @@ GIT sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) +GIT + remote: https://github.com/resque/redis-namespace.git + revision: 1403f511f6ae1ec9a8f330298a4cacf73fb10afc + specs: + redis-namespace (1.5.3) + redis (>= 3.0.4) + GIT remote: https://github.com/robin850/sdoc.git revision: 0e340352f3ab2f196c8a8743f83c2ee286e4f71c @@ -361,9 +389,7 @@ GEM rb-fsevent (0.10.2) rdoc (5.1.0) redcarpet (3.2.3) - redis (3.3.3) - redis-namespace (1.5.3) - redis (~> 3.0, >= 3.0.4) + redis (4.0.1) representable (3.0.4) declarative (< 0.1.0) declarative-option (< 0.2.0) @@ -374,11 +400,6 @@ GEM redis-namespace (~> 1.3) sinatra (>= 0.9.2) vegas (~> 0.1.2) - resque-scheduler (4.3.0) - mono_logger (~> 1.0) - redis (~> 3.3) - resque (~> 1.26) - rufus-scheduler (~> 3.2) retriable (3.1.1) rubocop (0.49.1) parallel (~> 1.10) @@ -403,11 +424,6 @@ GEM sequel (4.49.0) serverengine (1.5.11) sigdump (~> 0.2.2) - sidekiq (5.0.4) - concurrent-ruby (~> 1.0) - connection_pool (~> 2.2, >= 2.2.0) - rack-protection (>= 1.5.0) - redis (~> 3.3, >= 3.3.3) sigdump (0.2.4) signet (0.7.3) addressable (~> 2.3) @@ -520,14 +536,15 @@ DEPENDENCIES rake (>= 11.1) rb-inotify! redcarpet (~> 3.2.3) - redis + redis (~> 4.0) + redis-namespace! resque - resque-scheduler + resque-scheduler! rubocop (>= 0.47) sass-rails! sdoc! sequel - sidekiq + sidekiq! sneakers sprockets-export sqlite3 (~> 1.3.6) diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md index 560ee89846..3952887b61 100644 --- a/actioncable/CHANGELOG.md +++ b/actioncable/CHANGELOG.md @@ -1,3 +1,7 @@ +* Support redis-rb 4.0. + + *Jeremy Daer* + * Hash long stream identifiers when using PostgreSQL adapter. PostgreSQL has a limit on identifiers length (63 chars, [docs](https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS)). diff --git a/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb b/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb index 07774810ce..1227c793a9 100644 --- a/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb +++ b/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb @@ -3,7 +3,7 @@ require "thread" gem "em-hiredis", "~> 0.3.0" -gem "redis", "~> 3.0" +gem "redis", ">= 3", "< 5" require "em-hiredis" require "redis" diff --git a/actioncable/lib/action_cable/subscription_adapter/redis.rb b/actioncable/lib/action_cable/subscription_adapter/redis.rb index facea944ff..c28951608f 100644 --- a/actioncable/lib/action_cable/subscription_adapter/redis.rb +++ b/actioncable/lib/action_cable/subscription_adapter/redis.rb @@ -2,7 +2,7 @@ require "thread" -gem "redis", "~> 3.0" +gem "redis", ">= 3", "< 5" require "redis" module ActionCable @@ -76,7 +76,7 @@ module ActionCable def listen(conn) conn.without_reconnect do - original_client = conn.client + original_client = conn.respond_to?(:_client) ? conn._client : conn.client conn.subscribe("_action_cable_internal") do |on| on.subscribe do |chan, count| diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md index 77dfdefc05..425ab0c789 100644 --- a/activejob/CHANGELOG.md +++ b/activejob/CHANGELOG.md @@ -1,3 +1,7 @@ +* Support redis-rb 4.0. + + *Jeremy Daer* + * Change logging instrumentation to log errors when a job raises an exception. Fixes #26848. diff --git a/activejob/test/support/integration/adapters/resque.rb b/activejob/test/support/integration/adapters/resque.rb index 484b476567..7d5174b957 100644 --- a/activejob/test/support/integration/adapters/resque.rb +++ b/activejob/test/support/integration/adapters/resque.rb @@ -3,7 +3,7 @@ module ResqueJobsManager def setup ActiveJob::Base.queue_adapter = :resque - Resque.redis = Redis::Namespace.new "active_jobs_int_test", redis: Redis.connect(url: "redis://:password@127.0.0.1:6379/12", thread_safe: true) + Resque.redis = Redis::Namespace.new "active_jobs_int_test", redis: Redis.new(url: "redis://:password@127.0.0.1:6379/12", thread_safe: true) Resque.logger = Rails.logger unless can_run? puts "Cannot run integration tests for resque. To be able to run integration tests for resque you need to install and start redis.\n" @@ -41,11 +41,8 @@ module ResqueJobsManager end def can_run? - begin - Resque.redis.client.connect - rescue - return false - end - true + Resque.redis.ping == "PONG" + rescue + false end end diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index ff440b7939..20603dedaf 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,7 @@ +* Gemfile for new apps: upgrade redis-rb from ~> 3.0 to 4.0. + + *Jeremy Daer* + * Add `mini_magick` to default `Gemfile` as comment. *Yoshiyuki Hirano* diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index c8688fb7f3..6fb4ea52b3 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -369,7 +369,7 @@ module Rails return [] if options[:skip_action_cable] comment = "Use Redis adapter to run Action Cable in production" gems = [] - gems << GemfileEntry.new("redis", "~> 3.0", comment, {}, true) + gems << GemfileEntry.new("redis", "~> 4.0", comment, {}, true) gems end -- cgit v1.2.3 From 00f2d39c2e3111bf695aed9335388f0e06904d6a Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 9 Oct 2017 07:05:26 +0900 Subject: Remove passing redundant `self` to internal `apply_join_dependency` etc --- .../lib/active_record/relation/finder_methods.rb | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 5d4b549470..5ccfe3d1b7 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -310,12 +310,12 @@ module ActiveRecord return false if !conditions || limit_value == 0 - relation = self unless eager_loading? - relation ||= apply_join_dependency(self, construct_join_dependency(eager_loading: false)) - - return false if ActiveRecord::NullRelation === relation + if eager_loading? + relation = apply_join_dependency(construct_join_dependency(eager_loading: false)) + return relation.exists?(conditions) + end - relation = construct_relation_for_exists(relation, conditions) + relation = construct_relation_for_exists(conditions) skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false rescue ::RangeError @@ -368,15 +368,14 @@ module ActiveRecord # join_dependency = construct_join_dependency(joins_values) - aliases = join_dependency.aliases - relation = select aliases.columns - relation = apply_join_dependency(relation, join_dependency) + relation = apply_join_dependency(join_dependency) + relation._select!(join_dependency.aliases.columns) yield relation, join_dependency end - def construct_relation_for_exists(relation, conditions) - relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1) + def construct_relation_for_exists(conditions) + relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1) case conditions when Array, Hash @@ -396,11 +395,11 @@ module ActiveRecord end def construct_relation_for_association_calculations - apply_join_dependency(self, construct_join_dependency(joins_values)) + apply_join_dependency(construct_join_dependency(joins_values)) end - def apply_join_dependency(relation, join_dependency) - relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency) + def apply_join_dependency(join_dependency) + relation = except(:includes, :eager_load, :preload).joins!(join_dependency) if using_limitable_reflections?(join_dependency.reflections) relation -- cgit v1.2.3 From e50d4c9dedc75835c17211f337c0c08d2a5f58a0 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 9 Oct 2017 08:52:34 +0900 Subject: Fix typo in loading error message s/Action Record/Active Record/ --- .../lib/active_record/connection_adapters/connection_specification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index e9bda1885b..508132accb 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -200,7 +200,7 @@ module ActiveRecord # Bubbled up from the adapter require. Prefix the exception message # with some guidance about how to address it and reraise. else - raise e.class, "Error loading the '#{spec[:adapter]}' Action Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace + raise e.class, "Error loading the '#{spec[:adapter]}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace end end -- cgit v1.2.3 From f8337575ac1be1efb2bc89225a369e19fa2a234d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 9 Oct 2017 09:41:14 +0900 Subject: Missing revision 5efd21bb7ee4b326d9c0d26fccad10fc59dd51f0 in redis-rb-4.0 branch Seems the branch has rebased. cc @jeremy --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5ed069c13c..e3b30b84c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,7 +8,7 @@ GIT GIT remote: https://github.com/jeremy/resque-scheduler.git - revision: 5efd21bb7ee4b326d9c0d26fccad10fc59dd51f0 + revision: 7cccf7d8db4dbbf54bf549a98b6cbb38106dbed2 branch: redis-rb-4.0 specs: resque-scheduler (4.3.0) -- cgit v1.2.3 From 723b526158df576f673c1bc28439744378a03f5d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 2 Oct 2017 06:39:42 +0900 Subject: Fix `relation.exists?` with has_many through associations `relation.exists?` should reference correct aliases while joining tables of has_many through associations. --- activerecord/lib/active_record/relation/finder_methods.rb | 8 ++++---- activerecord/test/cases/finder_test.rb | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 5ccfe3d1b7..6e07402979 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -366,7 +366,7 @@ module ActiveRecord # preexisting join in joins_values to categorizations (by way of # the `has_many :through` for categories). # - join_dependency = construct_join_dependency(joins_values) + join_dependency = construct_join_dependency relation = apply_join_dependency(join_dependency) relation._select!(join_dependency.aliases.columns) @@ -387,15 +387,15 @@ module ActiveRecord relation end - def construct_join_dependency(joins = [], eager_loading: true) + def construct_join_dependency(eager_loading: true) including = eager_load_values + includes_values ActiveRecord::Associations::JoinDependency.new( - klass, table, including, alias_tracker(joins), eager_loading: eager_loading + klass, table, including, alias_tracker(joins_values), eager_loading: eager_loading ) end def construct_relation_for_association_calculations - apply_join_dependency(construct_join_dependency(joins_values)) + apply_join_dependency(construct_join_dependency) end def apply_join_dependency(join_dependency) diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 55496147c1..2f6d708092 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -9,6 +9,7 @@ require "models/company" require "models/tagging" require "models/topic" require "models/reply" +require "models/rating" require "models/entrant" require "models/project" require "models/developer" @@ -244,6 +245,13 @@ class FinderTest < ActiveRecord::TestCase assert_equal true, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(1).exists? end + def test_exists_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association + assert_nothing_raised do + developer = developers(:david) + developer.ratings.includes(comment: :post).where(posts: { id: 1 }).exists? + end + end + def test_exists_with_empty_table_and_no_args_given Topic.delete_all assert_equal false, Topic.exists? -- cgit v1.2.3 From c3d1fd13da0996182389c4ae83e971134e6d81f0 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Mon, 9 Oct 2017 09:42:43 +0900 Subject: Fix the result of `rails routes` in Guide [ci skip] --- guides/source/getting_started.md | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index a1ae515055..b007baea87 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -372,16 +372,17 @@ singular form `article` and makes meaningful use of the distinction. ```bash $ bin/rails routes - Prefix Verb URI Pattern Controller#Action - articles GET /articles(.:format) articles#index - POST /articles(.:format) articles#create - new_article GET /articles/new(.:format) articles#new -edit_article GET /articles/:id/edit(.:format) articles#edit - article GET /articles/:id(.:format) articles#show - PATCH /articles/:id(.:format) articles#update - PUT /articles/:id(.:format) articles#update - DELETE /articles/:id(.:format) articles#destroy - root GET / welcome#index + Prefix Verb URI Pattern Controller#Action +welcome_index GET /welcome/index(.:format) welcome#index + articles GET /articles(.:format) articles#index + POST /articles(.:format) articles#create + new_article GET /articles/new(.:format) articles#new + edit_article GET /articles/:id/edit(.:format) articles#edit + article GET /articles/:id(.:format) articles#show + PATCH /articles/:id(.:format) articles#update + PUT /articles/:id(.:format) articles#update + DELETE /articles/:id(.:format) articles#destroy + root GET / welcome#index ``` In the next section, you will add the ability to create new articles in your @@ -567,15 +568,16 @@ To see what Rails will do with this, we look back at the output of ```bash $ bin/rails routes Prefix Verb URI Pattern Controller#Action - articles GET /articles(.:format) articles#index - POST /articles(.:format) articles#create - new_article GET /articles/new(.:format) articles#new -edit_article GET /articles/:id/edit(.:format) articles#edit - article GET /articles/:id(.:format) articles#show - PATCH /articles/:id(.:format) articles#update - PUT /articles/:id(.:format) articles#update - DELETE /articles/:id(.:format) articles#destroy - root GET / welcome#index +welcome_index GET /welcome/index(.:format) welcome#index + articles GET /articles(.:format) articles#index + POST /articles(.:format) articles#create + new_article GET /articles/new(.:format) articles#new + edit_article GET /articles/:id/edit(.:format) articles#edit + article GET /articles/:id(.:format) articles#show + PATCH /articles/:id(.:format) articles#update + PUT /articles/:id(.:format) articles#update + DELETE /articles/:id(.:format) articles#destroy + root GET / welcome#index ``` The `articles_path` helper tells Rails to point the form to the URI Pattern -- cgit v1.2.3 From bb5b672ac6b4dd694a3f706d98d82f3000ce18cd Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 9 Oct 2017 10:30:03 +0900 Subject: Remove meaningless named `construct_relation_for_association_calculations` I don't think this is a good abstraction because the internal method is used only if the relation need to be applied join dependency. --- activerecord/lib/active_record/relation/calculations.rb | 5 +++-- activerecord/lib/active_record/relation/finder_methods.rb | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 0889d61c92..4ef0502893 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -130,7 +130,7 @@ module ActiveRecord # end def calculate(operation, column_name) if has_include?(column_name) - relation = construct_relation_for_association_calculations + relation = apply_join_dependency(construct_join_dependency) relation.distinct! if operation.to_s.downcase == "count" relation.calculate(operation, column_name) @@ -180,7 +180,8 @@ module ActiveRecord end if has_include?(column_names.first) - construct_relation_for_association_calculations.pluck(*column_names) + relation = apply_join_dependency(construct_join_dependency) + relation.pluck(*column_names) else relation = spawn relation.select_values = column_names.map { |cn| diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 6e07402979..707245bab2 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -394,10 +394,6 @@ module ActiveRecord ) end - def construct_relation_for_association_calculations - apply_join_dependency(construct_join_dependency) - end - def apply_join_dependency(join_dependency) relation = except(:includes, :eager_load, :preload).joins!(join_dependency) -- cgit v1.2.3 From 639fded77b5797b7770c99a76a40c141cc95c178 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Mon, 9 Oct 2017 10:11:03 +0900 Subject: Use `form_with` instead of `form_for` in engine guide [ci skip] --- guides/source/action_controller_overview.md | 6 +++--- guides/source/engines.md | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 014559f36b..d53c4dedf9 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -784,9 +784,9 @@ The way this is done is to add a non-guessable token which is only known to your If you generate a form like this: ```erb -<%= form_for @user do |f| %> - <%= f.text_field :username %> - <%= f.text_field :password %> +<%= form_with model: @user, local: true do |form| %> + <%= form.text_field :username %> + <%= form.text_field :password %> <% end %> ``` diff --git a/guides/source/engines.md b/guides/source/engines.md index 738de5d588..9f39832a7e 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -537,12 +537,12 @@ directory at `app/views/blorgh/comments` and in it a new file called ```html+erb

New comment

-<%= form_for [@article, @article.comments.build] do |f| %> +<%= form_with(model: [@article, @article.comments.build], local: true) do |form| %>

- <%= f.label :text %>
- <%= f.text_area :text %> + <%= form.label :text %>
+ <%= form.text_area :text %>

- <%= f.submit %> + <%= form.submit %> <% end %> ``` @@ -783,8 +783,8 @@ added above the `title` field with this code: ```html+erb
- <%= f.label :author_name %>
- <%= f.text_field :author_name %> + <%= form.label :author_name %>
+ <%= form.text_field :author_name %>
``` -- cgit v1.2.3 From b1e7bb9ed7ddbb29fd788dee1c3f2ba2626bd7ac Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 2 Oct 2017 06:39:05 +0900 Subject: All test cases for `exists?` places in `finder_test.rb` to ease to find the test cases --- activerecord/test/cases/finder_test.rb | 26 ++++++++++++++++++++++++++ activerecord/test/cases/relations_test.rb | 26 -------------------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 2f6d708092..d8bc917e7f 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -157,6 +157,32 @@ class FinderTest < ActiveRecord::TestCase assert_raise(NoMethodError) { Topic.exists?([1, 2]) } end + def test_exists_with_scope + davids = Author.where(name: "David") + assert_equal true, davids.exists? + assert_equal true, davids.exists?(authors(:david).id) + assert_equal false, davids.exists?(authors(:mary).id) + assert_equal false, davids.exists?("42") + assert_equal false, davids.exists?(42) + assert_equal false, davids.exists?(davids.new.id) + + fake = Author.where(name: "fake author") + assert_equal false, fake.exists? + assert_equal false, fake.exists?(authors(:david).id) + end + + def test_exists_uses_existing_scope + post = authors(:david).posts.first + authors = Author.includes(:posts).where(name: "David", posts: { id: post.id }) + assert_equal true, authors.exists?(authors(:david).id) + end + + def test_any_with_scope_on_hash_includes + post = authors(:david).posts.first + categories = Categorization.includes(author: :posts).where(posts: { id: post.id }) + assert_equal true, categories.exists? + end + def test_exists_with_polymorphic_relation post = Post.create!(title: "Post", body: "default", taggings: [Tagging.new(comment: "tagging comment")]) relation = Post.tagged_with_comment("tagging comment") diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index e1788be351..72433d1e8e 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -845,32 +845,6 @@ class RelationTest < ActiveRecord::TestCase } end - def test_exists - davids = Author.where(name: "David") - assert davids.exists? - assert davids.exists?(authors(:david).id) - assert ! davids.exists?(authors(:mary).id) - assert ! davids.exists?("42") - assert ! davids.exists?(42) - assert ! davids.exists?(davids.new.id) - - fake = Author.where(name: "fake author") - assert ! fake.exists? - assert ! fake.exists?(authors(:david).id) - end - - def test_exists_uses_existing_scope - post = authors(:david).posts.first - authors = Author.includes(:posts).where(name: "David", posts: { id: post.id }) - assert authors.exists?(authors(:david).id) - end - - def test_any_with_scope_on_hash_includes - post = authors(:david).posts.first - categories = Categorization.includes(author: :posts).where(posts: { id: post.id }) - assert categories.exists? - end - def test_last authors = Author.all assert_equal authors(:bob), authors.last -- cgit v1.2.3 From c5ab6e51a7b9ee05a2d262a72c7130b9c1d1b0ce Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 9 Oct 2017 15:18:59 +0900 Subject: Joined tables in association scope doesn't use the same aliases with the parent relation's aliases Building association scope in join dependency should respect the parent relation's aliases to avoid using the same alias name more than once. Fixes #30681. --- .../active_record/associations/alias_tracker.rb | 7 +++---- .../active_record/associations/join_dependency.rb | 7 ++++--- .../join_dependency/join_association.rb | 17 +++++++++++------ activerecord/lib/active_record/relation.rb | 3 ++- .../lib/active_record/relation/query_methods.rb | 22 +++++++++++----------- .../has_many_through_associations_test.rb | 4 ++++ activerecord/test/models/author.rb | 1 + 7 files changed, 36 insertions(+), 25 deletions(-) diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index 358cdabc7f..a8d8ee268a 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -30,6 +30,8 @@ module ActiveRecord ).size elsif join.respond_to? :left join.left.table_name == name ? 1 : 0 + elsif join.is_a?(Hash) + join[name] else # this branch is reached by two tests: # @@ -73,10 +75,7 @@ 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 :aliases + attr_reader :aliases private diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index b70b680885..df4bf07999 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -43,8 +43,6 @@ module ActiveRecord Column = Struct.new(:name, :alias) end - attr_reader :alias_tracker, :base_klass, :join_root - def self.make_tree(associations) hash = {} walk_tree associations, hash @@ -158,6 +156,9 @@ module ActiveRecord parents.values end + protected + attr_reader :alias_tracker, :base_klass, :join_root + private def make_constraints(parent, child, tables, join_type) @@ -224,7 +225,7 @@ module ActiveRecord raise EagerLoadPolymorphicError.new(reflection) end - JoinAssociation.new reflection, build(right, reflection.klass) + JoinAssociation.new(reflection, build(right, reflection.klass), alias_tracker) end.compact end diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index a526468bf6..a007d0cf5f 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -11,11 +11,12 @@ module ActiveRecord attr_accessor :tables - def initialize(reflection, children) + def initialize(reflection, children, alias_tracker) super(reflection.klass, children) - @reflection = reflection - @tables = nil + @alias_tracker = alias_tracker + @reflection = reflection + @tables = nil end def match?(other) @@ -38,11 +39,12 @@ module ActiveRecord joins << table.create_join(table, table.create_on(constraint), join_type) join_scope = reflection.join_scope(table, foreign_klass) + arel = join_scope.arel(alias_tracker.aliases) - if join_scope.arel.constraints.any? - joins.concat join_scope.arel.join_sources + if arel.constraints.any? + joins.concat arel.join_sources right = joins.last.right - right.expr = right.expr.and(join_scope.arel.constraints) + right.expr = right.expr.and(arel.constraints) end # The current table in this iteration becomes the foreign table in the next @@ -55,6 +57,9 @@ module ActiveRecord def table tables.first end + + protected + attr_reader :alias_tracker end end end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 1f59a0c53a..997cfe4b5e 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -551,7 +551,8 @@ module ActiveRecord limit_value || offset_value end - def alias_tracker(joins = []) # :nodoc: + def alias_tracker(joins = [], aliases = nil) # :nodoc: + joins += [aliases] if aliases ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index fb5beeffef..578d8a6eb7 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -898,8 +898,8 @@ module ActiveRecord end # Returns the Arel object associated with the relation. - def arel # :nodoc: - @arel ||= build_arel + def arel(aliases = nil) # :nodoc: + @arel ||= build_arel(aliases) end protected @@ -921,11 +921,11 @@ module ActiveRecord raise ImmutableRelation if defined?(@arel) && @arel end - def build_arel + def build_arel(aliases) arel = Arel::SelectManager.new(table) - build_joins(arel, joins_values.flatten) unless joins_values.empty? - build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty? + build_joins(arel, joins_values.flatten, aliases) unless joins_values.empty? + build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases) unless left_outer_joins_values.empty? arel.where(where_clause.ast) unless where_clause.empty? arel.having(having_clause.ast) unless having_clause.empty? @@ -970,7 +970,7 @@ module ActiveRecord end end - def build_left_outer_joins(manager, outer_joins) + def build_left_outer_joins(manager, outer_joins, aliases) buckets = outer_joins.group_by do |join| case join when Hash, Symbol, Array @@ -980,10 +980,10 @@ module ActiveRecord end end - build_join_query(manager, buckets, Arel::Nodes::OuterJoin) + build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases) end - def build_joins(manager, joins) + def build_joins(manager, joins, aliases) buckets = joins.group_by do |join| case join when String @@ -999,10 +999,10 @@ module ActiveRecord end end - build_join_query(manager, buckets, Arel::Nodes::InnerJoin) + build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases) end - def build_join_query(manager, buckets, join_type) + def build_join_query(manager, buckets, join_type, aliases) buckets.default = [] association_joins = buckets[:association_join] @@ -1013,7 +1013,7 @@ module ActiveRecord join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins) join_dependency = ActiveRecord::Associations::JoinDependency.new( - klass, table, association_joins, alias_tracker(join_list) + klass, table, association_joins, alias_tracker(join_list, aliases) ) joins = join_dependency.join_constraints(stashed_association_joins, join_type) 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 521b388cee..c6a4ac356f 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1250,6 +1250,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase TenantMembership.current_member = nil end + def test_has_many_trough_with_scope_that_has_joined_same_table_with_parent_relation + assert_equal authors(:david), Author.joins(:comments_for_first_author).take + end + def test_has_many_through_with_unscope_should_affect_to_through_scope assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments end diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 3371fcbfcc..e9eba9be2e 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -22,6 +22,7 @@ class Author < ActiveRecord::Base has_many :comments_containing_the_letter_e, through: :posts, source: :comments has_many :comments_with_order_and_conditions, -> { order("comments.body").where("comments.body like 'Thank%'") }, through: :posts, source: :comments has_many :comments_with_include, -> { includes(:post).where(posts: { type: "Post" }) }, through: :posts, source: :comments + has_many :comments_for_first_author, -> { for_first_author }, through: :posts, source: :comments has_many :first_posts has_many :comments_on_first_posts, -> { order("posts.id desc, comments.id asc") }, through: :first_posts, source: :comments -- cgit v1.2.3 From 24560d79fcf709fb871957e775dd02ad5a19eb39 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 9 Oct 2017 20:49:55 +0900 Subject: Use released `sidekiq` instead of master version The sidekiq 5.0.5 includes redis-rb 4.0 support. Ref: https://github.com/mperham/sidekiq/blob/90db3b84208cbb73a50d1a77a1dea97d3490ce70/Changes.md#505 --- Gemfile | 2 +- Gemfile.lock | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index aaf55f18c9..1ef6629945 100644 --- a/Gemfile +++ b/Gemfile @@ -62,7 +62,7 @@ gem "bootsnap", ">= 1.1.0", require: false group :job do gem "resque", require: false gem "resque-scheduler", github: "jeremy/resque-scheduler", branch: "redis-rb-4.0", require: false - gem "sidekiq", github: "mperham/sidekiq", require: false + gem "sidekiq", require: false gem "sucker_punch", require: false gem "delayed_job", require: false gem "queue_classic", github: "QueueClassic/queue_classic", branch: "master", require: false, platforms: :ruby diff --git a/Gemfile.lock b/Gemfile.lock index e3b30b84c2..9c3a7e3c82 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,16 +34,6 @@ GIT event_emitter websocket -GIT - remote: https://github.com/mperham/sidekiq.git - revision: 6332b9f8a316cf1000246701e40e108d16fed6d4 - specs: - sidekiq (5.0.5) - concurrent-ruby (~> 1.0) - connection_pool (~> 2.2, >= 2.2.0) - rack-protection (>= 1.5.0) - redis (>= 3.3.4, < 5) - GIT remote: https://github.com/rails/arel.git revision: 42510bf71472e2e35d9becb546edd05562672344 @@ -424,6 +414,11 @@ GEM sequel (4.49.0) serverengine (1.5.11) sigdump (~> 0.2.2) + sidekiq (5.0.5) + concurrent-ruby (~> 1.0) + connection_pool (~> 2.2, >= 2.2.0) + rack-protection (>= 1.5.0) + redis (>= 3.3.4, < 5) sigdump (0.2.4) signet (0.7.3) addressable (~> 2.3) @@ -544,7 +539,7 @@ DEPENDENCIES sass-rails! sdoc! sequel - sidekiq! + sidekiq sneakers sprockets-export sqlite3 (~> 1.3.6) -- cgit v1.2.3 From 04a7b7165ad204014c5850f62c921f7291d6ba5d Mon Sep 17 00:00:00 2001 From: Michael Coyne Date: Mon, 9 Oct 2017 21:08:38 -0400 Subject: Update security guide for signed cookie rotations The example was slightly incorrect. This commit also adds a test case for this example to cookies middleware unit tests. --- actionpack/test/dispatch/cookies_test.rb | 19 +++++++++++++++++++ guides/source/security.md | 5 +++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 2051f7546f..40cbad3b0d 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -917,6 +917,25 @@ class CookiesTest < ActionController::TestCase assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) end + def test_rotating_signed_cookies_digest + @request.env["action_dispatch.signed_cookie_digest"] = "SHA256" + @request.env["action_dispatch.cookies_rotations"].rotate :signed, digest: "SHA1" + + key_generator = @request.env["action_dispatch.key_generator"] + + old_secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + old_value = ActiveSupport::MessageVerifier.new(old_secret).generate(45) + + @request.headers["Cookie"] = "user_id=#{old_value}" + get :get_signed_cookie + + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA256") + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + def test_legacy_hmac_aes_cbc_encrypted_marshal_cookie_is_upgraded_to_authenticated_encrypted_cookie key_generator = @request.env["action_dispatch.key_generator"] encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] diff --git a/guides/source/security.md b/guides/source/security.md index bf9af88c5d..cfa777d433 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -169,11 +169,12 @@ you would first assign the new configuration value: Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256" ``` -Then you'd set up a rotation with the old configuration to keep it alive. +Now add a rotation for the old SHA1 digest so existing cookies are +seamlessly upgraded to the new SHA256 digest. ```ruby Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| - cookies.rotate :signed, digest: "SHA256" + cookies.rotate :signed, digest: "SHA1" end ``` -- cgit v1.2.3 From 8e314160a40c32abeab889777661b1a9bb6ae815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 10 Oct 2017 10:23:41 -0400 Subject: Fix documentation [ci skip] --- activesupport/lib/active_support/message_encryptor.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 8a1918039c..0cc4f58453 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -77,7 +77,7 @@ module ActiveSupport # Though if both the secret and the cipher was changed at the same time, # the above should be combined into: # - # verifier.rotate old_secret, cipher: "aes-256-cbc" + # crypt.rotate old_secret, cipher: "aes-256-cbc" class MessageEncryptor prepend Messages::Rotator::Encryptor -- cgit v1.2.3 From 0db6a14ae16b143e078375ff7f3c940cf707290b Mon Sep 17 00:00:00 2001 From: Tim Masliuchenko Date: Tue, 10 Oct 2017 14:15:56 +0300 Subject: Add allow_other_host option to redirect_back method --- .../lib/action_controller/metal/redirecting.rb | 23 +++++++++++++++------- actionpack/test/controller/redirect_test.rb | 21 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 5cd8568d8d..b8a80eef31 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -79,15 +79,18 @@ module ActionController # redirect_back fallback_location: "/images/screenshot.jpg" # redirect_back fallback_location: posts_url # redirect_back fallback_location: proc { edit_post_url(@post) } + # redirect_back fallback_location: '/', allow_other_host: false # - # All options that can be passed to redirect_to are accepted as + # ==== Options + # * :fallback_location - The default fallback location that will be used on missing `Referer` header. + # * :allow_other_host - Allows or dissallow redirection to the host that is different to the current host + # + # All other options that can be passed to redirect_to are accepted as # options and the behavior is identical. - def redirect_back(fallback_location:, **args) - if referer = request.headers["Referer"] - redirect_to referer, **args - else - redirect_to fallback_location, **args - end + def redirect_back(fallback_location:, allow_other_host: true, **args) + referer = request.headers["Referer"] + redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer)) + redirect_to redirect_to_referer ? referer : fallback_location, **args end def _compute_redirect_to_location(request, options) #:nodoc: @@ -120,5 +123,11 @@ module ActionController 302 end end + + def _url_host_allowed?(url) + URI(url.to_s).host == request.host + rescue ArgumentError, URI::Error + false + end end end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index e447b66486..2959dc3e4d 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -62,6 +62,10 @@ class RedirectController < ActionController::Base redirect_back(fallback_location: "/things/stuff", status: 307) end + def safe_redirect_back_with_status + redirect_back(fallback_location: "/things/stuff", status: 307, allow_other_host: false) + end + def host_redirect redirect_to action: "other_host", only_path: false, host: "other.test.host" end @@ -259,6 +263,23 @@ class RedirectTest < ActionController::TestCase assert_equal "http://test.host/things/stuff", redirect_to_url end + def test_safe_redirect_back_from_other_host + @request.env["HTTP_REFERER"] = "http://another.host/coming/from" + get :safe_redirect_back_with_status + + assert_response 307 + assert_equal "http://test.host/things/stuff", redirect_to_url + end + + def test_safe_redirect_back_from_the_same_host + referer = "http://test.host/coming/from" + @request.env["HTTP_REFERER"] = referer + get :safe_redirect_back_with_status + + assert_response 307 + assert_equal referer, redirect_to_url + end + def test_redirect_to_record with_routing do |set| set.draw do -- cgit v1.2.3 From b1ee5e8e7f98b32196423a144e7a87ae284c7a5c Mon Sep 17 00:00:00 2001 From: Mike Boone Date: Tue, 10 Oct 2017 23:08:03 -0400 Subject: Fix some typos. --- actionpack/lib/action_controller/metal/redirecting.rb | 2 +- actionview/lib/action_view/helpers/tag_helper.rb | 2 +- actionview/lib/action_view/test_case.rb | 4 ++-- activerecord/lib/active_record/callbacks.rb | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index b8a80eef31..8de57f9199 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -83,7 +83,7 @@ module ActionController # # ==== Options # * :fallback_location - The default fallback location that will be used on missing `Referer` header. - # * :allow_other_host - Allows or dissallow redirection to the host that is different to the current host + # * :allow_other_host - Allows or disallow redirection to the host that is different to the current host # # All other options that can be passed to redirect_to are accepted as # options and the behavior is identical. diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb index a64d7e396e..a6cec3f69c 100644 --- a/actionview/lib/action_view/helpers/tag_helper.rb +++ b/actionview/lib/action_view/helpers/tag_helper.rb @@ -166,7 +166,7 @@ module ActionView # This may come in handy when using jQuery's HTML5-aware .data() # from 1.4.3. # - # tag.div data: { city_state: %w( Chigaco IL ) } + # tag.div data: { city_state: %w( Chicago IL ) } # # =>
# # The generated attributes are escaped by default. This can be disabled using diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb index 93be2be2d1..e1cbae5845 100644 --- a/actionview/lib/action_view/test_case.rb +++ b/actionview/lib/action_view/test_case.rb @@ -270,7 +270,7 @@ module ActionView begin routes = @controller.respond_to?(:_routes) && @controller._routes rescue - # Dont call routes, if there is an error on _routes call + # Don't call routes, if there is an error on _routes call end if routes && @@ -286,7 +286,7 @@ module ActionView begin routes = @controller.respond_to?(:_routes) && @controller._routes rescue - # Dont call routes, if there is an error on _routes call + # Don't call routes, if there is an error on _routes call end routes && diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index a2439e6ec7..ac1070e65b 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -240,7 +240,7 @@ module ActiveRecord # # private # - # def log_chidren + # def log_children # # Child processing # end # @@ -265,7 +265,7 @@ module ActiveRecord # # private # - # def log_chidren + # def log_children # # Child processing # end # -- cgit v1.2.3 From b0691e6f6c9f309c359e8738ea01860528878f31 Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 11 Oct 2017 15:31:54 -0400 Subject: Update Gemfile --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 1ef6629945..1826c232fb 100644 --- a/Gemfile +++ b/Gemfile @@ -94,6 +94,7 @@ group :cable do gem "sprockets-export", require: false end +# Active Storage group :storage do gem "aws-sdk-s3", require: false gem "google-cloud-storage", "~> 1.3", require: false -- cgit v1.2.3 From 621e1a21f3f09de43885501741500faadea10b05 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Thu, 12 Oct 2017 09:10:58 +0900 Subject: Remove a needless space in Action View Guide [ci skip] --- guides/source/action_view_overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index 349108c207..c1e02745de 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -1160,7 +1160,7 @@ Returns a string of option tags for pretty much any time zone in the world. Returns select and option tags for the given object and method, using `time_zone_options_for_select` to generate the list of option tags. ```ruby -time_zone_select( "user", "time_zone") +time_zone_select("user", "time_zone") ``` #### date_field -- cgit v1.2.3 From 73c991c411d2afeaee7a7d6f4443fa687e86608a Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 12 Oct 2017 15:04:33 +0900 Subject: Fix "warning: assigned but unused variable - message" Ruby 2.5 warns about this. Ref: https://travis-ci.org/rails/rails/jobs/286338999 --- railties/test/generators/argv_scrubber_test.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/railties/test/generators/argv_scrubber_test.rb b/railties/test/generators/argv_scrubber_test.rb index 3f4c985a33..9ef61dc978 100644 --- a/railties/test/generators/argv_scrubber_test.rb +++ b/railties/test/generators/argv_scrubber_test.rb @@ -82,9 +82,8 @@ module Rails file.puts "--hello --world" file.flush - message = nil scrubber = Class.new(ARGVScrubber) { - define_method(:puts) { |msg| message = msg } + define_method(:puts) { |msg| } }.new ["new", "--rc=#{file.path}"] args = scrubber.prepare! assert_equal ["--hello", "--world"], args -- cgit v1.2.3 From d79e102bfaefc0dce843a73a48456831bd7848b7 Mon Sep 17 00:00:00 2001 From: willnet Date: Thu, 12 Oct 2017 16:45:21 +0900 Subject: [ci skip]Enable link to ActionDispatch::Integration::Session#process in rdoc --- .../lib/action_dispatch/testing/integration.rb | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index ae1f368e8b..8caa71199e 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -12,38 +12,38 @@ require_relative "request_encoder" module ActionDispatch module Integration #:nodoc: module RequestHelpers - # Performs a GET request with the given parameters. See +#process+ for more - # details. + # Performs a GET request with the given parameters. See ActionDispatch::Integration::Session#process + # for more details. def get(path, **args) process(:get, path, **args) end - # Performs a POST request with the given parameters. See +#process+ for more - # details. + # Performs a POST request with the given parameters. See ActionDispatch::Integration::Session#process + # for more details. def post(path, **args) process(:post, path, **args) end - # Performs a PATCH request with the given parameters. See +#process+ for more - # details. + # Performs a PATCH request with the given parameters. See ActionDispatch::Integration::Session#process + # for more details. def patch(path, **args) process(:patch, path, **args) end - # Performs a PUT request with the given parameters. See +#process+ for more - # details. + # Performs a PUT request with the given parameters. See ActionDispatch::Integration::Session#process + # for more details. def put(path, **args) process(:put, path, **args) end - # Performs a DELETE request with the given parameters. See +#process+ for - # more details. + # Performs a DELETE request with the given parameters. See ActionDispatch::Integration::Session#process + # for more details. def delete(path, **args) process(:delete, path, **args) end - # Performs a HEAD request with the given parameters. See +#process+ for more - # details. + # Performs a HEAD request with the given parameters. See ActionDispatch::Integration::Session#process + # for more details. def head(path, *args) process(:head, path, *args) end -- cgit v1.2.3 From 0efc132a8153dbaf0951ce55457abe43dd5a309b Mon Sep 17 00:00:00 2001 From: Aditya Kapoor Date: Thu, 12 Oct 2017 15:54:09 +0530 Subject: fix the description for the `select_all` [ci skip] --- guides/source/active_record_querying.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 3573c3c77b..3786343fc3 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -1712,10 +1712,10 @@ Client.find_by_sql("SELECT * FROM clients ### `select_all` -`find_by_sql` has a close relative called `connection#select_all`. `select_all` will retrieve objects from the database using custom SQL just like `find_by_sql` but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record. +`find_by_sql` has a close relative called `connection#select_all`. `select_all` will retrieve objects from the database using custom SQL just like `find_by_sql` but will not instantiate them. This method will return an instance of `ActiveRecord::Result` class and calling `to_hash` on this object would return you an array of hashes where each hash indicates a record. ```ruby -Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'") +Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'").to_hash # => [ # {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"}, # {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"} -- cgit v1.2.3 From 445c682a8465b1a42f1335ae2cf7d20b9a112fcd Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Thu, 12 Oct 2017 11:47:21 -0400 Subject: Introduce ActiveStorage::Blob#representation --- activestorage/app/models/active_storage/blob.rb | 26 ++++++++++++++ activestorage/app/models/active_storage/variant.rb | 4 +++ activestorage/test/models/preview_test.rb | 2 ++ activestorage/test/models/representation_test.rb | 41 ++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 activestorage/test/models/representation_test.rb diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index ff785d4f61..d43049e32c 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -15,6 +15,7 @@ # If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one. class ActiveStorage::Blob < ActiveRecord::Base class UnpreviewableError < StandardError; end + class UnrepresentableError < StandardError; end self.table_name = "active_storage_blobs" @@ -155,6 +156,31 @@ class ActiveStorage::Blob < ActiveRecord::Base end + # Returns an ActiveStorage::Preview instance for a previewable blob or an ActiveStorage::Variant instance for an image blob. + # + # blob.representation(resize: "100x100").processed.service_url + # + # Raises ActiveStorage::Blob::UnrepresentableError if the receiving blob is neither an image nor previewable. Call + # ActiveStorage::Blob#representable? to determine whether a blob is representable. + # + # See ActiveStorage::Blob#preview and ActiveStorage::Blob#variant for more information. + def representation(transformations) + case + when previewable? + preview transformations + when image? + variant transformations + else + raise UnrepresentableError + end + end + + # Returns true if the blob is an image or is previewable. + def representable? + image? || previewable? + end + + # Returns the URL of the blob on the service. This URL is intended to be short-lived for security and not used directly # with users. Instead, the +service_url+ should only be exposed as a redirect from a stable, possibly authenticated URL. # Hiding the +service_url+ behind a redirect also gives you the power to change services without updating all URLs. And diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb index 90a3605331..915b78162c 100644 --- a/activestorage/app/models/active_storage/variant.rb +++ b/activestorage/app/models/active_storage/variant.rb @@ -65,6 +65,10 @@ class ActiveStorage::Variant service.url key, expires_in: expires_in, disposition: disposition, filename: blob.filename, content_type: blob.content_type end + # Returns the receiving variant. Allows ActiveStorage::Variant and ActiveStorage::Preview instances to be duck-typed. + def image + self + end private def processed? diff --git a/activestorage/test/models/preview_test.rb b/activestorage/test/models/preview_test.rb index 317a2d5c58..bcd8442f4b 100644 --- a/activestorage/test/models/preview_test.rb +++ b/activestorage/test/models/preview_test.rb @@ -7,6 +7,7 @@ class ActiveStorage::PreviewTest < ActiveSupport::TestCase test "previewing a PDF" do blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf") preview = blob.preview(resize: "640x280").processed + assert preview.image.attached? assert_equal "report.png", preview.image.filename.to_s assert_equal "image/png", preview.image.content_type @@ -19,6 +20,7 @@ class ActiveStorage::PreviewTest < ActiveSupport::TestCase test "previewing an MP4 video" do blob = create_file_blob(filename: "video.mp4", content_type: "video/mp4") preview = blob.preview(resize: "640x280").processed + assert preview.image.attached? assert_equal "video.png", preview.image.filename.to_s assert_equal "image/png", preview.image.content_type diff --git a/activestorage/test/models/representation_test.rb b/activestorage/test/models/representation_test.rb new file mode 100644 index 0000000000..29fe61aee4 --- /dev/null +++ b/activestorage/test/models/representation_test.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +class ActiveStorage::RepresentationTest < ActiveSupport::TestCase + test "representing an image" do + blob = create_file_blob + representation = blob.representation(resize: "100x100").processed + + image = read_image(representation.image) + assert_equal 100, image.width + assert_equal 67, image.height + end + + test "representing a PDF" do + blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf") + representation = blob.representation(resize: "640x280").processed + + image = read_image(representation.image) + assert_equal 612, image.width + assert_equal 792, image.height + end + + test "representing an MP4 video" do + blob = create_file_blob(filename: "video.mp4", content_type: "video/mp4") + representation = blob.representation(resize: "640x280").processed + + image = read_image(representation.image) + assert_equal 640, image.width + assert_equal 480, image.height + end + + test "representing an unrepresentable blob" do + blob = create_blob + + assert_raises ActiveStorage::Blob::UnrepresentableError do + blob.representation resize: "100x100" + end + end +end -- cgit v1.2.3 From 62ff514d33d3a3b0930956a4b4866e6b228c278c Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Thu, 12 Oct 2017 13:40:25 -0400 Subject: Accept variation keys in #preview and #variant --- activestorage/app/models/active_storage/blob.rb | 4 ++-- activestorage/app/models/active_storage/variation.rb | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index d43049e32c..84b8f3827b 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -124,7 +124,7 @@ class ActiveStorage::Blob < ActiveRecord::Base # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::VariantsController # can then produce on-demand. def variant(transformations) - ActiveStorage::Variant.new(self, ActiveStorage::Variation.new(transformations)) + ActiveStorage::Variant.new(self, ActiveStorage::Variation.wrap(transformations)) end @@ -144,7 +144,7 @@ class ActiveStorage::Blob < ActiveRecord::Base # whether a blob is accepted by any previewer, call ActiveStorage::Blob#previewable?. def preview(transformations) if previewable? - ActiveStorage::Preview.new(self, ActiveStorage::Variation.new(transformations)) + ActiveStorage::Preview.new(self, ActiveStorage::Variation.wrap(transformations)) else raise UnpreviewableError end diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb index df2643442a..13bad87cac 100644 --- a/activestorage/app/models/active_storage/variation.rb +++ b/activestorage/app/models/active_storage/variation.rb @@ -13,16 +13,21 @@ class ActiveStorage::Variation attr_reader :transformations class << self - def wrap(variation_or_key) - case variation_or_key + # Returns a Variation instance based on the given variator. If the variator is a Variation, it is + # returned unmodified. If it is a String, it is passed to ActiveStorage::Variation.decode. Otherwise, + # it is assumed to be a transformations Hash and is passed directly to the constructor. + def wrap(variator) + case variator when self - variation_or_key + variator + when String + decode variator else - decode variation_or_key + new variator end end - # Returns a variation instance with the transformations that were encoded by +encode+. + # Returns a Variation instance with the transformations that were encoded by +encode+. def decode(key) new ActiveStorage.verifier.verify(key, purpose: :variation) end -- cgit v1.2.3 From 0149481b1b29ac15197f1abe8a3961d8cb2926b6 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Fri, 13 Oct 2017 08:59:20 +0900 Subject: Capitalize "sprockets" in Engine guide [ci skip] --- guides/source/engines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/engines.md b/guides/source/engines.md index 9f39832a7e..a9b841e3bf 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -1361,7 +1361,7 @@ that only exists for your engine. In this case, the host application doesn't need to require `admin.css` or `admin.js`. Only the gem's admin layout needs these assets. It doesn't make sense for the host app to include `"blorgh/admin.css"` in its stylesheets. In this situation, you should -explicitly define these assets for precompilation. This tells sprockets to add +explicitly define these assets for precompilation. This tells Sprockets to add your engine assets when `bin/rails assets:precompile` is triggered. You can define assets for precompilation in `engine.rb`: -- cgit v1.2.3 From 34d7b05d277e96d31cab40f7818dcc2e8b107a30 Mon Sep 17 00:00:00 2001 From: Kazunori Kajihiro Date: Fri, 13 Oct 2017 11:55:36 +0900 Subject: Test exception message to ensure an exception instance is yielded --- activejob/test/cases/exceptions_test.rb | 2 +- activejob/test/jobs/retry_job.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activejob/test/cases/exceptions_test.rb b/activejob/test/cases/exceptions_test.rb index 7a3c372143..22fed0a808 100644 --- a/activejob/test/cases/exceptions_test.rb +++ b/activejob/test/cases/exceptions_test.rb @@ -61,7 +61,7 @@ class ExceptionsTest < ActiveJob::TestCase test "custom handling of job that exceeds retry attempts" do perform_enqueued_jobs do RetryJob.perform_later "CustomCatchError", 6 - assert_equal "Dealt with a job that failed to retry in a custom way after 6 attempts", JobBuffer.last_value + assert_equal "Dealt with a job that failed to retry in a custom way after 6 attempts. Message: CustomCatchError", JobBuffer.last_value end end diff --git a/activejob/test/jobs/retry_job.rb b/activejob/test/jobs/retry_job.rb index a12d09779b..9aa99d9a21 100644 --- a/activejob/test/jobs/retry_job.rb +++ b/activejob/test/jobs/retry_job.rb @@ -17,7 +17,7 @@ class RetryJob < ActiveJob::Base retry_on ShortWaitTenAttemptsError, wait: 1.second, attempts: 10 retry_on ExponentialWaitTenAttemptsError, wait: :exponentially_longer, attempts: 10 retry_on CustomWaitTenAttemptsError, wait: ->(executions) { executions * 2 }, attempts: 10 - retry_on(CustomCatchError) { |job, exception| JobBuffer.add("Dealt with a job that failed to retry in a custom way after #{job.arguments.second} attempts") } + retry_on(CustomCatchError) { |job, exception| JobBuffer.add("Dealt with a job that failed to retry in a custom way after #{job.arguments.second} attempts. Message: #{exception.message}") } discard_on DiscardableError def perform(raising, attempts) -- cgit v1.2.3 From 2e0de2dd41234a59b028af3ed486549bda6ea438 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Fri, 13 Oct 2017 11:52:33 +0900 Subject: Bump RuboCop 0.50.0 RuboCop 0.50.0 was released. https://github.com/bbatsov/rubocop/releases/tag/v0.50.0 And `rubocop-0-50` channel is available in Code Climate. https://github.com/codeclimate/codeclimate-rubocop/issues/107#issuecomment-336234260 This commit will bump RuboCop to 0.50.0. There are no new offences in this change. ```console % bundle exec rubocop --version 0.50.0 % bundle exec rubocop Inspecting 2350 files (snip) 2350 files inspected, no offenses detected ``` --- .codeclimate.yml | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.codeclimate.yml b/.codeclimate.yml index 63d3562e9b..1f4a1bb201 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,7 +1,7 @@ engines: rubocop: enabled: true - channel: rubocop-0-49 + channel: rubocop-0-50 ratings: paths: diff --git a/Gemfile.lock b/Gemfile.lock index 9c3a7e3c82..6228d754a8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -391,14 +391,14 @@ GEM sinatra (>= 0.9.2) vegas (~> 0.1.2) retriable (3.1.1) - rubocop (0.49.1) + rubocop (0.50.0) parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) - rainbow (>= 1.99.1, < 3.0) + rainbow (>= 2.2.2, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) - ruby-progressbar (1.8.1) + ruby-progressbar (1.9.0) ruby_dep (1.5.0) rubyzip (1.2.1) rufus-scheduler (3.4.2) -- cgit v1.2.3 From d0aeeb732866ef906f2d7539e9ef165acc7ce64b Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 13 Oct 2017 18:08:35 +0900 Subject: Remove unsued `jquery-rails` This has been added by 8f8cb1baa3b5609969805fcdd7295f3d7de2bd6b. But now it is unnecessary because it is not used in the test. --- Gemfile | 1 - Gemfile.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/Gemfile b/Gemfile index 1ef6629945..8c6d943e18 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,6 @@ gem "mocha", require: false gem "capybara", "~> 2.15" gem "rack-cache", "~> 1.2" -gem "jquery-rails" gem "coffee-rails" gem "sass-rails", github: "rails/sass-rails", branch: "5-0-stable" gem "turbolinks", "~> 5" diff --git a/Gemfile.lock b/Gemfile.lock index 6228d754a8..ac56bca4ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -290,10 +290,6 @@ GEM httpclient (2.8.3) i18n (0.8.6) jmespath (1.3.1) - jquery-rails (4.3.1) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) json (2.1.0) jwt (1.5.6) kindlerb (1.2.0) @@ -510,7 +506,6 @@ DEPENDENCIES erubis (~> 2.7.0) google-cloud-storage (~> 1.3) hiredis - jquery-rails json (>= 2.0.0) kindlerb (~> 1.2.0) libxml-ruby -- cgit v1.2.3 From 29da7d1ff510a9f376fc6c780273dfa89298ff51 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Fri, 13 Oct 2017 07:52:39 -0400 Subject: Clarify comment [ci skip] --- activestorage/app/models/active_storage/variant.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb index 915b78162c..fa5aa69bd3 100644 --- a/activestorage/app/models/active_storage/variant.rb +++ b/activestorage/app/models/active_storage/variant.rb @@ -65,7 +65,7 @@ class ActiveStorage::Variant service.url key, expires_in: expires_in, disposition: disposition, filename: blob.filename, content_type: blob.content_type end - # Returns the receiving variant. Allows ActiveStorage::Variant and ActiveStorage::Preview instances to be duck-typed. + # Returns the receiving variant. Allows ActiveStorage::Variant and ActiveStorage::Preview instances to be used interchangeably. def image self end -- cgit v1.2.3 From d06a1ee7eec96a1b52f21026e18a0a09219e66a8 Mon Sep 17 00:00:00 2001 From: Dimitri Roche Date: Fri, 13 Oct 2017 09:05:12 -0400 Subject: Update documentation to lead with ajax param `event.detail` --- guides/source/working_with_javascript_in_rails.md | 55 ++++++++--------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 098366ec1b..f8cfac3995 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -188,15 +188,19 @@ bind to the `ajax:success` event. On failure, use `ajax:error`. Check it out: ```coffeescript $(document).ready -> - $("#new_article").on("ajax:success", (e, data, status, xhr) -> + $("#new_article").on("ajax:success", (event) -> + [data, status, xhr] = event.detail $("#new_article").append xhr.responseText - ).on "ajax:error", (e, xhr, status, error) -> + ).on "ajax:error", (event) -> $("#new_article").append "

ERROR

" ``` Obviously, you'll want to be a bit more sophisticated than that, but it's a start. +NOTE: As of Rails 5.1 and the new `rails-ujs`, the parameters `e, data, status, xhr` +have been bundled into `event.detail`. + #### link_to [`link_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to) @@ -225,7 +229,7 @@ and write some CoffeeScript like this: ```coffeescript $ -> - $("a[data-remote]").on "ajax:success", (e, data, status, xhr) -> + $("a[data-remote]").on "ajax:success", (event) -> alert "The article was deleted." ``` @@ -343,39 +347,6 @@ This generates a form with: ``` -Dealing with Ajax events ------------------------- - -Here are the different events that are fired when you deal with elements -that have a `data-remote` attribute: - -NOTE: All handlers bound to these events are always passed the event object as the -first argument. The table below describes the extra parameters passed after the -event argument. For example, if the extra parameters are listed as `xhr, settings`, -then to access them, you would define your handler with `function(event, xhr, settings)`. - -| Event name | Extra parameters | Fired | -|---------------------|------------------|-------------------------------------------------------------| -| `ajax:before` | | Before the whole ajax business, aborts if stopped. | -| `ajax:beforeSend` | xhr, options | Before the request is sent, aborts if stopped. | -| `ajax:send` | xhr | When the request is sent. | -| `ajax:success` | xhr, status, err | After completion, if the response was a success. | -| `ajax:error` | xhr, status, err | After completion, if the response was an error. | -| `ajax:complete` | xhr, status | After the request has been completed, no matter the outcome.| -| `ajax:aborted:file` | elements | If there are non-blank file inputs, aborts if stopped. | - -### Stoppable events - -If you stop `ajax:before` or `ajax:beforeSend` by returning false from the -handler method, the Ajax request will never take place. The `ajax:before` event -is also useful for manipulating form data before serialization. The -`ajax:beforeSend` event is also useful for adding custom request headers. - -If you stop the `ajax:aborted:file` event, the default behavior of allowing the -browser to submit the form via normal means (i.e. non-Ajax submission) will be -canceled and the form will not be submitted at all. This is useful for -implementing your own Ajax file upload workaround. - ### Rails-ujs event handlers Rails 5.1 introduced rails-ujs and dropped jQuery as a dependency. @@ -405,6 +376,18 @@ document.body.addEventListener('ajax:success', function(event) { }) ``` +### Stoppable events + +If you stop `ajax:before` or `ajax:beforeSend` by returning false from the +handler method, the Ajax request will never take place. The `ajax:before` event +is also useful for manipulating form data before serialization. The +`ajax:beforeSend` event is also useful for adding custom request headers. + +If you stop the `ajax:aborted:file` event, the default behavior of allowing the +browser to submit the form via normal means (i.e. non-Ajax submission) will be +canceled and the form will not be submitted at all. This is useful for +implementing your own Ajax file upload workaround. + Server-Side Concerns -------------------- -- cgit v1.2.3 From edf167e84829e505b89e32f6ef4987f720f19627 Mon Sep 17 00:00:00 2001 From: Pierre Hedkvist Date: Fri, 13 Oct 2017 13:21:11 +0000 Subject: Added test case for starting rails with daemon option, this should set the option[:daemonize] to true, otherwise the option[:daemonize] will be set to false --- railties/test/commands/server_test.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index a6201e4f04..60bb8ca1db 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -22,6 +22,18 @@ class Rails::ServerTest < ActiveSupport::TestCase assert_nil options[:server] end + def test_server_option_with_daemon + args = ["-d"] + options = parse_arguments(args) + assert_equal true, options[:daemonize] + end + + def test_server_option_without_daemon + args = [] + options = parse_arguments(args) + assert_equal false, options[:daemonize] + end + def test_server_option_without_environment args = ["thin"] with_rack_env nil do -- cgit v1.2.3 From bf5e693fd184cbbcfd56667f6f528313de2f35ae Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Fri, 13 Oct 2017 22:26:13 +0900 Subject: Add process.action_mailer notification to Instrumentation guide [ci skip] --- guides/source/active_support_instrumentation.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 3b31557f41..82168ffbdb 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -337,6 +337,22 @@ Action Mailer } ``` +### process.action_mailer + +| Key | Value | +| ------------- | ------------------------ | +| `:mailer` | Name of the mailer class | +| `:action` | The action | +| `:args` | The arguments | + +```ruby +{ + mailer: "Notification", + action: "welcome_email", + args: [] +} +``` + Active Support -------------- -- cgit v1.2.3 From 8c3e345521e1b5f45de65a201dcc7e11e1ead8b0 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Fri, 13 Oct 2017 23:23:45 +0900 Subject: Add Action Cable notifications to instrument guide [ci skip] --- guides/source/active_support_instrumentation.md | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 82168ffbdb..390d989566 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -466,6 +466,45 @@ Active Job | `:adapter` | QueueAdapter object processing the job | | `:job` | Job object | +Action Cable +------------ + +### perform_action.action_cable + +| Key | Value | +| ---------------- | ------------------------- | +| `:channel_class` | Name of the channel class | +| `:action` | The action | +| `:data` | A hash of data | + +### transmit.action_cable + +| Key | Value | +| ---------------- | ------------------------- | +| `:channel_class` | Name of the channel class | +| `:data` | A hash of data | +| `:via` | Via | + +### transmit_subscription_confirmation.action_cable + +| Key | Value | +| ---------------- | ------------------------- | +| `:channel_class` | Name of the channel class | + +### transmit_subscription_rejection.action_cable + +| Key | Value | +| ---------------- | ------------------------- | +| `:channel_class` | Name of the channel class | + +### broadcast.action_cable + +| Key | Value | +| --------------- | -------------------- | +| `:broadcasting` | A named broadcasting | +| `:message` | A hash of message | +| `:coder` | The coder | + Active Storage -------------- -- cgit v1.2.3 From f1e47b0348bd6c18dd94659af549a25684ca78ff Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Fri, 13 Oct 2017 20:47:56 +0300 Subject: Define path with __dir__ in activestorage/ Related to #29176 --- activestorage/test/database/setup.rb | 2 +- activestorage/test/dummy/bin/bundle | 2 +- activestorage/test/test_helper.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activestorage/test/database/setup.rb b/activestorage/test/database/setup.rb index 87564499e6..705650a25d 100644 --- a/activestorage/test/database/setup.rb +++ b/activestorage/test/database/setup.rb @@ -3,5 +3,5 @@ require_relative "create_users_migration" ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") -ActiveRecord::Migrator.migrate File.expand_path("../../../db/migrate", __FILE__) +ActiveRecord::Migrator.migrate File.expand_path("../../db/migrate", __dir__) ActiveStorageCreateUsers.migrate(:up) diff --git a/activestorage/test/dummy/bin/bundle b/activestorage/test/dummy/bin/bundle index 277e128251..5015ba6f8b 100755 --- a/activestorage/test/dummy/bin/bundle +++ b/activestorage/test/dummy/bin/bundle @@ -1,5 +1,5 @@ #!/usr/bin/env ruby # frozen_string_literal: true -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__) +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) load Gem.bin_path("bundler", "bundle") diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb index dd37239060..e206cf1b8b 100644 --- a/activestorage/test/test_helper.rb +++ b/activestorage/test/test_helper.rb @@ -23,7 +23,7 @@ Minitest.backtrace_filter = Minitest::BacktraceFilter.new require "yaml" SERVICE_CONFIGURATIONS = begin - erb = ERB.new(Pathname.new(File.expand_path("../service/configurations.yml", __FILE__)).read) + erb = ERB.new(Pathname.new(File.expand_path("service/configurations.yml", __dir__)).read) configuration = YAML.load(erb.result) || {} configuration.deep_symbolize_keys rescue Errno::ENOENT @@ -38,7 +38,7 @@ ActiveStorage::Service.logger = ActiveSupport::Logger.new(nil) ActiveStorage.verifier = ActiveSupport::MessageVerifier.new("Testing") class ActiveSupport::TestCase - self.file_fixture_path = File.expand_path("../fixtures/files", __FILE__) + self.file_fixture_path = File.expand_path("fixtures/files", __dir__) private def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain") -- cgit v1.2.3 From 19323d3bcc828e36b2d7ce23bc0e0031a05a012e Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Fri, 13 Oct 2017 20:50:03 +0300 Subject: Use `require_relative` instead of `require` with full path in activestorage/ Related to #29417 --- activestorage/test/test_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb index e206cf1b8b..60656feb80 100644 --- a/activestorage/test/test_helper.rb +++ b/activestorage/test/test_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require File.expand_path("../../test/dummy/config/environment.rb", __FILE__) +require_relative "dummy/config/environment.rb" require "bundler/setup" require "active_support" -- cgit v1.2.3 From 6a7559417f91429f9f2006c5406e0387923c7312 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 14 Oct 2017 07:27:12 +0900 Subject: Fix test name for daemon option test In this test file, "server option" refers to the server used to start Rails(e.g. `puma`, `thin`). But this test, "server option" is not specified. Therefore, I think that it is incorrect that `server_option` is included in the test name. --- railties/test/commands/server_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 60bb8ca1db..33715ea75f 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -22,13 +22,13 @@ class Rails::ServerTest < ActiveSupport::TestCase assert_nil options[:server] end - def test_server_option_with_daemon + def test_daemon_with_option args = ["-d"] options = parse_arguments(args) assert_equal true, options[:daemonize] end - def test_server_option_without_daemon + def test_daemon_without_option args = [] options = parse_arguments(args) assert_equal false, options[:daemonize] -- cgit v1.2.3 From 92a19f76719ec67d469b58dc05b6ebf6a184c6b4 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sat, 14 Oct 2017 08:11:14 +0900 Subject: Add unpermitted_parameters.action_controller hook to instrument guide [ci skip] --- guides/source/active_support_instrumentation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 390d989566..25f78fd940 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -197,6 +197,12 @@ INFO. Additional keys may be added by the caller. } ``` +### unpermitted_parameters.action_controller + +| Key | Value | +| ------- | ---------------- | +| `:keys` | Unpermitted keys | + Action View ----------- -- cgit v1.2.3 From 3d1ff79742c46930fa35352c42fb585c3408511b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 14 Oct 2017 12:28:08 +0900 Subject: Show the failed queries in `test_has_one_does_not_use_order_by` For investigating the cause of failure. https://travis-ci.org/rails/rails/jobs/287474883#L797-L799 --- activerecord/test/cases/associations/has_one_associations_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 2a9ebd19ed..ec5d95080b 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -30,7 +30,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase ActiveRecord::SQLCounter.clear_log companies(:first_firm).account ensure - assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query" + log_all = ActiveRecord::SQLCounter.log_all + assert log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query: #{log_all}" end def test_has_one_cache_nils -- cgit v1.2.3 From 5668dc6b1863ef43be8f8ef0fb1d5db913085fb3 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 14 Oct 2017 13:19:26 +0900 Subject: Fix `COUNT(DISTINCT ...)` for `GROUP BY` with `ORDER BY` and `LIMIT` This is the fix for the regression of #29848. In #29848, I've kept existing select list in the subquery for the count if ORDER BY is given. But it had accidentally affect to GROUP BY queries also. It should keep the previous behavior in that case. Fixes #30886. --- activerecord/CHANGELOG.md | 6 ++++++ activerecord/lib/active_record/relation/calculations.rb | 2 +- activerecord/test/cases/calculations_test.rb | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 288f22a9d3..490908417a 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Fix `COUNT(DISTINCT ...)` for `GROUP BY` with `ORDER BY` and `LIMIT`. + + Fixes #30886. + + *Ryuta Kamizono* + * PostgreSQL `tsrange` now preserves subsecond precision. PostgreSQL 9.1+ introduced range types, and Rails added support for using diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 4ef0502893..116bddce85 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -216,7 +216,7 @@ module ActiveRecord if operation == "count" column_name ||= select_for_count if column_name == :all - if distinct && !(has_limit_or_offset? && order_values.any?) + if distinct && (group_values.any? || !(has_limit_or_offset? && order_values.any?)) column_name = primary_key end elsif column_name =~ /\s*DISTINCT[\s(]+/i diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index b47fd0af41..66bc14b5ab 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -260,6 +260,10 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).limit(3).offset(2).count end + def test_distinct_count_with_group_by_and_order_and_limit + assert_equal({ 6 => 2 }, Account.group(:firm_id).distinct.order("1 DESC").limit(1).count) + end + def test_should_group_by_summed_field_having_condition c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit) assert_nil c[1] -- cgit v1.2.3 From 1ac2a2292f1010662f83906b97d046974409c948 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Sun, 15 Oct 2017 02:11:18 +0900 Subject: Add accept-charset to the output of form_with in JS guide [ci skip] --- guides/source/working_with_javascript_in_rails.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 098366ec1b..b2716c7faa 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -174,7 +174,7 @@ passing the `:local` option `form_with`. This will generate the following HTML: ```html -
+ ...
``` -- cgit v1.2.3 From 879d540adc34603f0fd1ac1a44763598e9ccc551 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 15 Oct 2017 16:01:02 +0900 Subject: Remove unused `before_filters` This method added by 1008511. It is unnecessary because it is no longer called by 19c3495. --- actionpack/lib/action_controller/metal/testing.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb index b07f1f3d8c..6e8a95040f 100644 --- a/actionpack/lib/action_controller/metal/testing.rb +++ b/actionpack/lib/action_controller/metal/testing.rb @@ -12,11 +12,5 @@ module ActionController self.params = nil end end - - module ClassMethods - def before_filters - _process_action_callbacks.find_all { |x| x.kind == :before }.map(&:name) - end - end end end -- cgit v1.2.3 From 9493d4553569118b2a85da84fd3a8ba2b5b2de76 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 15 Oct 2017 20:18:12 +0900 Subject: MySQL: Don't lose `auto_increment: true` in the `db/schema.rb` Currently `AUTO_INCREMENT` is implicitly used in the default primary key definition. But `AUTO_INCREMENT` is not only used for single column primary key, but also for composite primary key. In that case, `auto_increment: true` should be dumped explicitly in the `db/schema.rb`. Fixes #30894. --- activerecord/CHANGELOG.md | 6 ++++ .../connection_adapters/mysql/schema_dumper.rb | 7 +++++ .../cases/adapters/mysql2/auto_increment_test.rb | 34 ++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 activerecord/test/cases/adapters/mysql2/auto_increment_test.rb diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 490908417a..0492694dac 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* MySQL: Don't lose `auto_increment: true` in the `db/schema.rb`. + + Fixes #30894. + + *Ryuta Kamizono* + * Fix `COUNT(DISTINCT ...)` for `GROUP BY` with `ORDER BY` and `LIMIT`. Fixes #30886. diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb index 95eb77aea4..d23178e43c 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb @@ -8,6 +8,7 @@ module ActiveRecord def prepare_column_options(column) spec = super spec[:unsigned] = "true" if column.unsigned? + spec[:auto_increment] = "true" if column.auto_increment? if @connection.supports_virtual_columns? && column.virtual? spec[:as] = extract_expression_for_virtual_column(column) @@ -18,6 +19,12 @@ module ActiveRecord spec end + def column_spec_for_primary_key(column) + spec = super + spec.delete(:auto_increment) if column.type == :integer && column.auto_increment? + spec + end + def default_primary_key?(column) super && column.auto_increment? && !column.unsigned? end diff --git a/activerecord/test/cases/adapters/mysql2/auto_increment_test.rb b/activerecord/test/cases/adapters/mysql2/auto_increment_test.rb new file mode 100644 index 0000000000..4c67633946 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/auto_increment_test.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "cases/helper" +require "support/schema_dumping_helper" + +class Mysql2AutoIncrementTest < ActiveRecord::Mysql2TestCase + include SchemaDumpingHelper + + def setup + @connection = ActiveRecord::Base.connection + end + + def teardown + @connection.drop_table :auto_increments, if_exists: true + end + + def test_auto_increment_without_primary_key + @connection.create_table :auto_increments, id: false, force: true do |t| + t.integer :id, null: false, auto_increment: true + t.index :id + end + output = dump_table_schema("auto_increments") + assert_match(/t\.integer\s+"id",\s+null: false,\s+auto_increment: true$/, output) + end + + def test_auto_increment_with_composite_primary_key + @connection.create_table :auto_increments, primary_key: [:id, :created_at], force: true do |t| + t.integer :id, null: false, auto_increment: true + t.datetime :created_at, null: false + end + output = dump_table_schema("auto_increments") + assert_match(/t\.integer\s+"id",\s+null: false,\s+auto_increment: true$/, output) + end +end -- cgit v1.2.3 From 8877492df2853cba92d95dabdfcdee7244e7d8f0 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 15 Oct 2017 23:22:33 +0900 Subject: Fix longer sequence name detection for serial columns (#28339) We already found the longer sequence name, but we could not consider whether it was the sequence name created by serial type due to missed a max identifier length limitation. I've addressed the sequence name consideration to respect the max identifier length. Fixes #28332. --- activerecord/CHANGELOG.md | 6 ++++ .../active_record/connection_adapters/column.rb | 2 +- .../connection_adapters/postgresql/column.rb | 20 ++++++++++++++ .../postgresql/schema_statements.rb | 3 +- .../connection_adapters/postgresql_adapter.rb | 5 ++-- .../test/cases/adapters/postgresql/serial_test.rb | 32 ++++++++++++++++++++++ 6 files changed, 64 insertions(+), 4 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 0492694dac..9cca2c4c5a 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Fix longer sequence name detection for serial columns. + + Fixes #28332. + + *Ryuta Kamizono* + * MySQL: Don't lose `auto_increment: true` in the `db/schema.rb`. Fixes #30894. diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 16273fb5f1..28d949b503 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -15,7 +15,7 @@ module ActiveRecord # +default+ is the type-casted default value, such as +new+ in sales_stage varchar(20) default 'new'. # +sql_type_metadata+ is various information about the type of the column # +null+ determines if this column allows +NULL+ values. - def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil) + def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil, **) @name = name.freeze @table_name = table_name @sql_type_metadata = sql_type_metadata diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb index ff95fa4a0e..469ef3f5a0 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb @@ -7,6 +7,11 @@ module ActiveRecord delegate :array, :oid, :fmod, to: :sql_type_metadata alias :array? :array + def initialize(*, max_identifier_length: 63, **) + super + @max_identifier_length = max_identifier_length + end + def serial? return unless default_function @@ -15,8 +20,23 @@ module ActiveRecord end end + protected + attr_reader :max_identifier_length + private def sequence_name_from_parts(table_name, column_name, suffix) + over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length + + if over_length > 0 + column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min + over_length -= column_name.length - column_name_length + column_name = column_name[0, column_name_length - [over_length, 0].min] + end + + if over_length > 0 + table_name = table_name[0, table_name.length - over_length] + end + "#{table_name}_#{column_name}_#{suffix}" 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 9e2f61e6ce..79cb4e276d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -617,7 +617,8 @@ module ActiveRecord table_name, default_function, collation, - comment: comment.presence + comment: comment.presence, + max_identifier_length: max_identifier_length ) end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 5b6ad5fbc4..eae3d59946 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -352,10 +352,11 @@ module ActiveRecord end # Returns the configured supported identifier length supported by PostgreSQL - def table_alias_length + def max_identifier_length @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i end - alias index_name_length table_alias_length + alias table_alias_length max_identifier_length + alias index_name_length max_identifier_length # Set the authorized user for this session def session_auth=(user) diff --git a/activerecord/test/cases/adapters/postgresql/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb index df7875dbf2..6a99323be5 100644 --- a/activerecord/test/cases/adapters/postgresql/serial_test.rb +++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb @@ -121,4 +121,36 @@ module SequenceNameDetectionTestCases assert_match %r{t\.bigserial\s+"bar_baz_id",\s+null: false$}, output end end + + class LongerSequenceNameDetectionTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + + def setup + @table_name = "long_table_name_to_test_sequence_name_detection_for_serial_cols" + @connection = ActiveRecord::Base.connection + @connection.create_table @table_name, force: true do |t| + t.serial :seq + t.bigserial :bigseq + end + end + + def teardown + @connection.drop_table @table_name, if_exists: true + end + + def test_serial_columns + columns = @connection.columns(@table_name) + columns.each do |column| + assert_equal :integer, column.type + assert column.serial? + end + end + + def test_schema_dump_with_long_table_name + output = dump_table_schema @table_name + assert_match %r{create_table "#{@table_name}", force: :cascade}, output + assert_match %r{t\.serial\s+"seq",\s+null: false$}, output + assert_match %r{t\.bigserial\s+"bigseq",\s+null: false$}, output + end + end end -- cgit v1.2.3 From 300c68e9396364db063e26473448803c6853e234 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 16 Oct 2017 00:20:47 +0900 Subject: Add a test case that eager-loading with a polymorphic association and using `exists?` This test covers the case of 02da8aea. Previously `exists?` was always eager-loading the includes values. But now it is eager-loaded only when necessary since 07a611e0. So the case of the eager-loading had not covered in the test. --- activerecord/test/cases/associations/eager_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 559f0e9338..9afe6a893c 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1501,6 +1501,10 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_equal posts(:welcome), post end + test "eager-loading with a polymorphic association and using the existential predicate" do + assert_equal true, authors(:david).essays.eager_load(:writer).exists? + end + # CollectionProxy#reader is expensive, so the preloader avoids calling it. test "preloading has_many_through association avoids calling association.reader" do ActiveRecord::Associations::HasManyAssociation.any_instance.expects(:reader).never -- cgit v1.2.3 From 8b52d4cfd26964e7d1b7d462253d399b243ab256 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 16 Oct 2017 03:22:34 +0900 Subject: Remove extra spaces in the args in the `time_zone_select` [ci skip] Follow up of #30862. --- actionview/lib/action_view/helpers/form_options_helper.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb index 1517abfad0..38b185f126 100644 --- a/actionview/lib/action_view/helpers/form_options_helper.rb +++ b/actionview/lib/action_view/helpers/form_options_helper.rb @@ -279,17 +279,17 @@ module ActionView # Finally, this method supports a :default option, which selects # a default ActiveSupport::TimeZone if the object's time zone is +nil+. # - # time_zone_select( "user", "time_zone", nil, include_blank: true) + # time_zone_select("user", "time_zone", nil, include_blank: true) # - # time_zone_select( "user", "time_zone", nil, default: "Pacific Time (US & Canada)" ) + # time_zone_select("user", "time_zone", nil, default: "Pacific Time (US & Canada)") # - # time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)") + # time_zone_select("user", 'time_zone', ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)") # - # time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ]) + # time_zone_select("user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ]) # - # time_zone_select( "user", 'time_zone', /Australia/) + # time_zone_select("user", 'time_zone', /Australia/) # - # time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, model: ActiveSupport::TimeZone) + # time_zone_select("user", "time_zone", ActiveSupport::TimeZone.all.sort, model: ActiveSupport::TimeZone) def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {}) Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render end -- cgit v1.2.3 From 99b2bf8db39846897f017129a41efcffa840dc68 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Tue, 12 Sep 2017 01:06:07 +0300 Subject: Fix `bin/rails db:setup` and `bin/rails db:test:prepare` create wrong ar_internal_metadata's data for a test database. Before: ``` $ RAILS_ENV=test rails dbconsole > SELECT * FROM ar_internal_metadata; key|value|created_at|updated_at environment|development|2017-09-11 23:14:10.815679|2017-09-11 23:14:10.815679 ``` After: ``` $ RAILS_ENV=test rails dbconsole > SELECT * FROM ar_internal_metadata; key|value|created_at|updated_at environment|test|2017-09-11 23:14:10.815679|2017-09-11 23:14:10.815679 ``` Fixes #26731. --- activerecord/CHANGELOG.md | 23 +++++++++++++ .../lib/active_record/railties/databases.rake | 4 +-- .../lib/active_record/tasks/database_tasks.rb | 15 +++++---- railties/test/application/rake/dbs_test.rb | 39 ++++++++++++++++++++++ 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 9cca2c4c5a..06d52ee1fa 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,26 @@ +* Fix `bin/rails db:setup` and `bin/rails db:test:prepare` create wrong + ar_internal_metadata's data for a test database. + + Before: + ``` + $ RAILS_ENV=test rails dbconsole + > SELECT * FROM ar_internal_metadata; + key|value|created_at|updated_at + environment|development|2017-09-11 23:14:10.815679|2017-09-11 23:14:10.815679 + ``` + + After: + ``` + $ RAILS_ENV=test rails dbconsole + > SELECT * FROM ar_internal_metadata; + key|value|created_at|updated_at + environment|test|2017-09-11 23:14:10.815679|2017-09-11 23:14:10.815679 + ``` + + Fixes #26731. + + *bogdanvlviv* + * Fix longer sequence name detection for serial columns. Fixes #28332. diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 691b3612d8..eb32c29b7e 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -313,7 +313,7 @@ db_namespace = namespace :db do begin should_reconnect = ActiveRecord::Base.connection_pool.active_connection? ActiveRecord::Schema.verbose = false - ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :ruby, ENV["SCHEMA"] + ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :ruby, ENV["SCHEMA"], "test" ensure if should_reconnect ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env]) @@ -323,7 +323,7 @@ db_namespace = namespace :db do # desc "Recreate the test database from an existent structure.sql file" task load_structure: %w(db:test:purge) do - ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :sql, ENV["SCHEMA"] + ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :sql, ENV["SCHEMA"], "test" end # desc "Empty the test database" diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 0f3f84ca08..0b85965bab 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -225,7 +225,7 @@ module ActiveRecord 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: + def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env) # :nodoc: file ||= schema_file(format) case format @@ -240,7 +240,7 @@ module ActiveRecord raise ArgumentError, "unknown format #{format.inspect}" end ActiveRecord::InternalMetadata.create_table - ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment + ActiveRecord::InternalMetadata[:environment] = environment end def schema_file(format = ActiveRecord::Base.schema_format) @@ -253,8 +253,8 @@ module ActiveRecord end def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env) - each_current_configuration(environment) { |configuration| - load_schema configuration, format, file + each_current_configuration(environment) { |configuration, configuration_environment| + load_schema configuration, format, file, configuration_environment } ActiveRecord::Base.establish_connection(environment.to_sym) end @@ -301,9 +301,10 @@ module ActiveRecord environments = [environment] environments << "test" if environment == "development" - configurations = ActiveRecord::Base.configurations.values_at(*environments) - configurations.compact.each do |configuration| - yield configuration unless configuration["database"].blank? + ActiveRecord::Base.configurations.slice(*environments).each do |configuration_environment, configuration| + next unless configuration["database"] + + yield configuration, configuration_environment end end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index b7b6ed8a0e..3057a0a3fb 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -298,6 +298,45 @@ module ApplicationTests ENV["RACK_ENV"] = @old_rack_env end end + + test "db:setup sets ar_internal_metadata" do + app_file "db/schema.rb", "" + rails "db:setup" + + test_environment = lambda { rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip } + development_environment = lambda { rails("runner", "puts ActiveRecord::InternalMetadata[:environment]").strip } + + assert_equal "test", test_environment.call + assert_equal "development", development_environment.call + + app_file "db/structure.sql", "" + app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY + Rails.application.config.active_record.schema_format = :sql + RUBY + + rails "db:setup" + + assert_equal "test", test_environment.call + assert_equal "development", development_environment.call + end + + test "db:test:prepare sets test ar_internal_metadata" do + app_file "db/schema.rb", "" + rails "db:test:prepare" + + test_environment = lambda { rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip } + + assert_equal "test", test_environment.call + + app_file "db/structure.sql", "" + app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY + Rails.application.config.active_record.schema_format = :sql + RUBY + + rails "db:test:prepare" + + assert_equal "test", test_environment.call + end end end end -- cgit v1.2.3 From 678e563da3e7cddca9501ec90cdd76ee399a62b1 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Tue, 12 Sep 2017 10:08:16 +0300 Subject: `ActiveRecord::Tasks::DatabaseTasks.load_schema` has always to establish database connection When load schema from `structure.sql`, database connection isn't established. `ActiveRecord::Tasks::DatabaseTasks.load_schema` has to establish database connection since it executes ``` ActiveRecord::InternalMetadata.create_table ActiveRecord::InternalMetadata[:environment] = environment ``` --- activerecord/lib/active_record/tasks/database_tasks.rb | 6 +++--- railties/test/application/rake/dbs_test.rb | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 0b85965bab..ff388ff1f6 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -228,13 +228,13 @@ module ActiveRecord def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env) # :nodoc: file ||= schema_file(format) + check_schema_file(file) + ActiveRecord::Base.establish_connection(configuration) + case format when :ruby - check_schema_file(file) - ActiveRecord::Base.establish_connection(configuration) load(file) when :sql - check_schema_file(file) structure_load(configuration, file) else raise ArgumentError, "unknown format #{format.inspect}" diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 3057a0a3fb..fd22477539 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -189,6 +189,14 @@ module ApplicationTests db_structure_dump_and_load database_url_db_name end + test "db:structure:dump and db:structure:load set ar_internal_metadata" do + require "#{app_path}/config/environment" + db_structure_dump_and_load ActiveRecord::Base.configurations[Rails.env]["database"] + + assert_equal "test", rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip + assert_equal "development", rails("runner", "puts ActiveRecord::InternalMetadata[:environment]").strip + end + test "db:structure:dump does not dump schema information when no migrations are used" do # create table without migrations rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" -- cgit v1.2.3 From 5ea4cae911604eb879f68f66fad4e83121d1b072 Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Mon, 16 Oct 2017 00:20:20 +0100 Subject: Remove association(true) references from docs [ci skip] Passing `true` to force an association to reload its records from the database was deprecated in 5.0 and removed in 5.1. --- activerecord/lib/active_record/associations.rb | 2 +- activerecord/lib/active_record/associations/collection_proxy.rb | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index ef26f4a20c..91f915183a 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -850,7 +850,7 @@ module ActiveRecord # project.milestones # fetches milestones from the database # project.milestones.size # uses the milestone cache # project.milestones.empty? # uses the milestone cache - # project.milestones(true).size # fetches milestones from the database + # project.milestones.reload.size # fetches milestones from the database # project.milestones # uses the milestone cache # # == Eager loading of associations diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 412e89255d..07f7303f8d 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -1072,7 +1072,6 @@ module ActiveRecord end # Reloads the collection from the database. Returns +self+. - # Equivalent to collection(true). # # class Person < ActiveRecord::Base # has_many :pets @@ -1086,9 +1085,6 @@ module ActiveRecord # # person.pets.reload # fetches pets from the database # # => [#] - # - # person.pets(true) # fetches pets from the database - # # => [#] def reload proxy_association.reload reset_scope -- cgit v1.2.3 From 5e6fa51b01a5da7a09c6365923d5cb2c16a7e7a6 Mon Sep 17 00:00:00 2001 From: Aditya Kapoor Date: Mon, 16 Oct 2017 18:16:23 +0530 Subject: Fix `to_s(:db)` for range comprising of alphabets. --- activesupport/lib/active_support/core_ext/range/conversions.rb | 8 +++++++- activesupport/test/core_ext/range_ext_test.rb | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb index 37868f5875..8832fbcb3c 100644 --- a/activesupport/lib/active_support/core_ext/range/conversions.rb +++ b/activesupport/lib/active_support/core_ext/range/conversions.rb @@ -2,7 +2,13 @@ module ActiveSupport::RangeWithFormat RANGE_FORMATS = { - db: Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" } + db: -> (start, stop) do + case start + when String then "BETWEEN '#{start}' AND '#{stop}'" + else + "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" + end + end } # Convert range to a formatted string. See RANGE_FORMATS for predefined formats. diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index a96e3d62e8..0467123e55 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -16,6 +16,11 @@ class RangeTest < ActiveSupport::TestCase assert_equal "BETWEEN '2005-12-10 15:30:00' AND '2005-12-10 17:30:00'", date_range.to_s(:db) end + def test_to_s_with_alphabets + alphabet_range = ("a".."z") + assert_equal "BETWEEN 'a' AND 'z'", alphabet_range.to_s(:db) + end + def test_to_s_with_numeric number_range = (1..100) assert_equal "BETWEEN '1' AND '100'", number_range.to_s(:db) -- cgit v1.2.3 From ada05850f84ee0eef5413950333e5b5332a64b48 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 13 Oct 2017 15:17:17 +0900 Subject: Add headless chrome driver to System Tests --- actionpack/CHANGELOG.md | 4 ++++ actionpack/lib/action_dispatch/system_test_case.rb | 2 ++ .../lib/action_dispatch/system_testing/driver.rb | 18 +++++++++++++++++- actionpack/test/abstract_unit.rb | 4 ++++ actionpack/test/dispatch/system_testing/driver_test.rb | 8 ++++++++ .../dispatch/system_testing/system_test_case_test.rb | 6 ++++++ 6 files changed, 41 insertions(+), 1 deletion(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index adb86aad9f..9a001cef9b 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add headless chrome support to System Tests. + + *Yuji Yaginuma* + * Add ability to enable Early Hints for HTTP/2 If supported by the server, and enabled in Puma this allows H2 Early Hints to be used. diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb index ae4aeac59d..3f8481ad48 100644 --- a/actionpack/lib/action_dispatch/system_test_case.rb +++ b/actionpack/lib/action_dispatch/system_test_case.rb @@ -121,6 +121,8 @@ module ActionDispatch # # driven_by :selenium, using: :firefox # + # driven_by :selenium, using: :headless_chrome + # # driven_by :selenium, screen_size: [800, 800] def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {}) self.driver = SystemTesting::Driver.new(driver, using: using, screen_size: screen_size, options: options) diff --git a/actionpack/lib/action_dispatch/system_testing/driver.rb b/actionpack/lib/action_dispatch/system_testing/driver.rb index 4279336f2f..770fbde74e 100644 --- a/actionpack/lib/action_dispatch/system_testing/driver.rb +++ b/actionpack/lib/action_dispatch/system_testing/driver.rb @@ -31,8 +31,24 @@ module ActionDispatch end end + def browser_options + if @browser == :headless_chrome + browser_options = Selenium::WebDriver::Chrome::Options.new + browser_options.args << "--headless" + browser_options.args << "--disable-gpu" + + @options.merge(options: browser_options) + else + @options + end + end + + def browser + @browser == :headless_chrome ? :chrome : @browser + end + def register_selenium(app) - Capybara::Selenium::Driver.new(app, { browser: @browser }.merge(@options)).tap do |driver| + Capybara::Selenium::Driver.new(app, { browser: browser }.merge(browser_options)).tap do |driver| driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size) end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index caa56018f8..34dc02bebf 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -449,3 +449,7 @@ end class DrivenBySeleniumWithChrome < ActionDispatch::SystemTestCase driven_by :selenium, using: :chrome end + +class DrivenBySeleniumWithHeadlessChrome < ActionDispatch::SystemTestCase + driven_by :selenium, using: :headless_chrome +end diff --git a/actionpack/test/dispatch/system_testing/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb index e6f9353b22..75feae6fe0 100644 --- a/actionpack/test/dispatch/system_testing/driver_test.rb +++ b/actionpack/test/dispatch/system_testing/driver_test.rb @@ -17,6 +17,14 @@ class DriverTest < ActiveSupport::TestCase assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) end + test "initializing the driver with a headless chrome" do + driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :headless_chrome, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" }) + assert_equal :selenium, driver.instance_variable_get(:@name) + assert_equal :headless_chrome, driver.instance_variable_get(:@browser) + assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size) + assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) + end + test "initializing the driver with a poltergeist" do driver = ActionDispatch::SystemTesting::Driver.new(:poltergeist, screen_size: [1400, 1400], options: { js_errors: false }) assert_equal :poltergeist, driver.instance_variable_get(:@name) diff --git a/actionpack/test/dispatch/system_testing/system_test_case_test.rb b/actionpack/test/dispatch/system_testing/system_test_case_test.rb index 771a2a8d6e..c6a6aef92b 100644 --- a/actionpack/test/dispatch/system_testing/system_test_case_test.rb +++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb @@ -22,6 +22,12 @@ class SetDriverToSeleniumTest < DrivenBySeleniumWithChrome end end +class SetDriverToSeleniumHeadlessChromeTest < DrivenBySeleniumWithHeadlessChrome + test "uses selenium headless chrome" do + assert_equal :selenium, Capybara.current_driver + end +end + class SetHostTest < DrivenByRackTest test "sets default host" do assert_equal "http://127.0.0.1", Capybara.app_host -- cgit v1.2.3 From 681fc2f552625f9a3a37b3eabdca827275761a21 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Tue, 17 Oct 2017 15:20:04 +0900 Subject: Update mailing list URL in I18n guide [ci skip] --- guides/source/i18n.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 0153f52249..e6aa6181cc 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -1171,7 +1171,7 @@ Conclusion At this point you should have a good overview about how I18n support in Ruby on Rails works and are ready to start translating your project. -If you want to discuss certain portions or have questions, please sign up to the [rails-i18n mailing list](http://groups.google.com/group/rails-i18n). +If you want to discuss certain portions or have questions, please sign up to the [rails-i18n mailing list](https://groups.google.com/forum/#!forum/rails-i18n). Contributing to Rails I18n @@ -1179,7 +1179,7 @@ Contributing to Rails I18n I18n support in Ruby on Rails was introduced in the release 2.2 and is still evolving. The project follows the good Ruby on Rails development tradition of evolving solutions in gems and real applications first, and only then cherry-picking the best-of-breed of most widely useful features for inclusion in the core. -Thus we encourage everybody to experiment with new ideas and features in gems or other libraries and make them available to the community. (Don't forget to announce your work on our [mailing list](http://groups.google.com/group/rails-i18n)!) +Thus we encourage everybody to experiment with new ideas and features in gems or other libraries and make them available to the community. (Don't forget to announce your work on our [mailing list](https://groups.google.com/forum/#!forum/rails-i18n)!) If you find your own locale (language) missing from our [example translations data](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) repository for Ruby on Rails, please [_fork_](https://github.com/guides/fork-a-project-and-submit-your-modifications) the repository, add your data and send a [pull request](https://help.github.com/articles/about-pull-requests/). @@ -1187,7 +1187,7 @@ If you find your own locale (language) missing from our [example translations da Resources --------- -* [Google group: rails-i18n](https://groups.google.com/group/rails-i18n) - The project's mailing list. +* [Google group: rails-i18n](https://groups.google.com/forum/#!forum/rails-i18n) - The project's mailing list. * [GitHub: rails-i18n](https://github.com/svenfuchs/rails-i18n) - Code repository and issue tracker for the rails-i18n project. Most importantly you can find lots of [example translations](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) for Rails that should work for your application in most cases. * [GitHub: i18n](https://github.com/svenfuchs/i18n) - Code repository and issue tracker for the i18n gem. -- cgit v1.2.3 From 3218d5f8407e902177fbffcea40cf7d5eb4d89a9 Mon Sep 17 00:00:00 2001 From: pavel Date: Tue, 17 Oct 2017 23:26:11 +0200 Subject: compatibility - use int instead of bigint --- activerecord/lib/active_record/migration/compatibility.rb | 2 +- activerecord/test/cases/migration/compatibility_test.rb | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index 2b247f7a7a..c979aaf0a0 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -171,7 +171,7 @@ module ActiveRecord class << t prepend TableDefinition end - t + super end def index_name_for_remove(table_name, options = {}) diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 1ae15eb439..b50d68d6df 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -71,6 +71,9 @@ module ActiveRecord assert_not connection.index_exists?(:more_testings, :foo_id) assert_not connection.index_exists?(:more_testings, :bar_id) + + legacy_ref = connection.columns(:more_testings).find { |c| c.name == "foo_id" } + assert_not legacy_ref.bigint? ensure connection.drop_table :more_testings rescue nil end -- cgit v1.2.3 From 3be123ba26cad461a80d7d680819e71c1388a241 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 18 Oct 2017 08:38:30 +0900 Subject: Should test `LegacyPrimaryKeyTest` to both `V5_0` and `V4_2` --- .../test/cases/migration/compatibility_test.rb | 49 +++++++++++++++------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index b50d68d6df..2fef2f796e 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -71,9 +71,6 @@ module ActiveRecord assert_not connection.index_exists?(:more_testings, :foo_id) assert_not connection.index_exists?(:more_testings, :bar_id) - - legacy_ref = connection.columns(:more_testings).find { |c| c.name == "foo_id" } - assert_not legacy_ref.bigint? ensure connection.drop_table :more_testings rescue nil end @@ -133,11 +130,9 @@ module ActiveRecord end end -class LegacyPrimaryKeyTest < ActiveRecord::TestCase +module LegacyPrimaryKeyTestCases include SchemaDumpingHelper - self.use_transactional_tests = false - class LegacyPrimaryKey < ActiveRecord::Base end @@ -155,7 +150,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase end def test_legacy_primary_key_should_be_auto_incremented - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_table :legacy_primary_keys do |t| t.references :legacy_ref @@ -185,7 +180,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase def test_legacy_integer_primary_key_should_not_be_auto_incremented skip if current_adapter?(:SQLite3Adapter) - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_table :legacy_primary_keys, id: :integer do |t| end @@ -204,7 +199,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter) def test_legacy_primary_key_in_create_table_should_be_integer - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_table :legacy_primary_keys, id: false do |t| t.primary_key :id @@ -219,7 +214,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase end def test_legacy_primary_key_in_change_table_should_be_integer - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_table :legacy_primary_keys, id: false do |t| t.integer :dummy @@ -237,7 +232,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase end def test_add_column_with_legacy_primary_key_should_be_integer - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_table :legacy_primary_keys, id: false do |t| t.integer :dummy @@ -254,7 +249,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase end def test_legacy_join_table_foreign_keys_should_be_integer - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_join_table :apples, :bananas do |t| end @@ -269,7 +264,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase end def test_legacy_join_table_column_options_should_be_overwritten - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_join_table :apples, :bananas, column_options: { type: :bigint } do |t| end @@ -285,7 +280,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase if current_adapter?(:Mysql2Adapter) def test_legacy_bigint_primary_key_should_be_auto_incremented - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_table :legacy_primary_keys, id: :bigint end @@ -302,7 +297,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase end else def test_legacy_bigint_primary_key_should_not_be_auto_incremented - @migration = Class.new(ActiveRecord::Migration[5.0]) { + @migration = Class.new(migration_class) { def change create_table :legacy_primary_keys, id: :bigint do |t| end @@ -320,3 +315,27 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase end end end + +module LegacyPrimaryKeyTest + class V5_0 < ActiveRecord::TestCase + include LegacyPrimaryKeyTestCases + + self.use_transactional_tests = false + + private + def migration_class + ActiveRecord::Migration[5.0] + end + end + + class V4_2 < ActiveRecord::TestCase + include LegacyPrimaryKeyTestCases + + self.use_transactional_tests = false + + private + def migration_class + ActiveRecord::Migration[4.2] + end + end +end -- cgit v1.2.3 From 116e2c67f5ca4518f2db80b761cc3d5f4993b12a Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 18 Oct 2017 14:31:49 +0900 Subject: Fix typo in `allow_failure` argument [ci skip] Ref: https://github.com/rails/rails/blob/3be123ba26cad461a80d7d680819e71c1388a241/railties/test/isolation/abstract_unit.rb#L243 --- railties/test/isolation/abstract_unit.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index b7f214cb73..29daaacdb2 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -235,7 +235,7 @@ module TestHelpers # Invoke a bin/rails command inside the app # - # allow_failures:: true to return normally if the command exits with + # allow_failure:: true to return normally if the command exits with # a non-zero status. By default, this method will raise. # stderr:: true to pass STDERR output straight to the "real" STDERR. # By default, the STDERR and STDOUT of the process will be -- cgit v1.2.3 From 3695bbaf5fe200875717c12f6f94a28121382494 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 18 Oct 2017 14:56:23 +0900 Subject: Remove unnecessary `allow_failure: true` option `routes` task always returns zero to status, so status is not to non-zeno. Ref: https://github.com/rails/rails/blob/b1867c480dd5476948ff0492ea2f91e2c2fcb04b/railties/lib/rails/tasks/routes.rake#L30 --- railties/test/application/rake_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index e74100ec93..bf89098645 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -166,7 +166,7 @@ module ApplicationTests end RUBY - output = rails("routes", "-g", "show", allow_failure: true) + output = rails("routes", "-g", "show") assert_equal <<-MESSAGE.strip_heredoc, output Prefix Verb URI Pattern Controller#Action cart GET /cart(.:format) cart#show -- cgit v1.2.3 From b1c8610fca6d8a59246e190bfaed1aa445480b07 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 18 Oct 2017 16:28:29 +0900 Subject: Remove unused `UnknownController` class `UnknownController` was added in b1999be, but it is not used anywhere. --- actionpack/lib/action_controller/metal/exceptions.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index f808295720..a65857d6ef 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -34,9 +34,6 @@ module ActionController class NotImplemented < MethodNotAllowed #:nodoc: end - class UnknownController < ActionControllerError #:nodoc: - end - class MissingFile < ActionControllerError #:nodoc: end -- cgit v1.2.3 From 64b45b2fe1b9f64517f88c3b69c851d9c60ad77c Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 18 Oct 2017 15:03:06 +0300 Subject: Add mention how to delete several records in bulk to Active Record Basics Guides [ci skip] --- guides/source/active_record_basics.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index 11aefcb05f..069a624984 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -304,6 +304,17 @@ user = User.find_by(name: 'David') user.destroy ``` +If you'd like to delete several records in bulk, you may use `destroy_all` +method: + +```ruby +# find and delete all users named David +User.where(name: 'David').destroy_all + +# delete all users +User.destroy_all +``` + Validations ----------- -- cgit v1.2.3 From 615415ba8145b82ff8d303db3add0ece572eaf24 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 18 Oct 2017 16:28:12 +0300 Subject: Set proper migration' version in bug report templates --- guides/bug_report_templates/active_record_migrations_gem.rb | 2 +- guides/bug_report_templates/active_record_migrations_master.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb index f75b6fd932..9002b8f428 100644 --- a/guides/bug_report_templates/active_record_migrations_gem.rb +++ b/guides/bug_report_templates/active_record_migrations_gem.rb @@ -37,7 +37,7 @@ end class Payment < ActiveRecord::Base end -class ChangeAmountToAddScale < ActiveRecord::Migration[5.0] +class ChangeAmountToAddScale < ActiveRecord::Migration[5.1] def change reversible do |dir| dir.up do diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb index 60416ed42f..fce8d1d848 100644 --- a/guides/bug_report_templates/active_record_migrations_master.rb +++ b/guides/bug_report_templates/active_record_migrations_master.rb @@ -37,7 +37,7 @@ end class Payment < ActiveRecord::Base end -class ChangeAmountToAddScale < ActiveRecord::Migration[5.0] +class ChangeAmountToAddScale < ActiveRecord::Migration[5.2] def change reversible do |dir| dir.up do -- cgit v1.2.3 From 25ea77d5089caaf8c2445044499720b13c91deda Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 18 Oct 2017 15:00:16 +0000 Subject: Enable `hstore` extention disabled at the end of `InvertibleMigrationTest#test_migrate_enable_and_disable_extension` to avoid failure of `PostgresqlArrayTest#test_schema_dump_with_shorthand` which expects `hstore` extension enabled. --- activerecord/test/cases/invertible_migration_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index 20e747142b..60c628511f 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -295,6 +295,8 @@ module ActiveRecord migration2.migrate(:down) assert_equal false, Horse.connection.extension_enabled?("hstore") + ensure + enable_extension!("hstore", ActiveRecord::Base.connection) end end -- cgit v1.2.3 From 62a38d8407355f468d2a9a3c6d0ab5d6914cd00a Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Wed, 18 Oct 2017 10:48:27 -0500 Subject: Improve out-of-box experience with System tests including chromedriver-helper by default --- railties/lib/rails/generators/rails/app/templates/Gemfile | 2 ++ railties/test/generators/app_generator_test.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index bfbba789b0..95e8be96b6 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -43,6 +43,8 @@ group :development, :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '~> 2.15' gem 'selenium-webdriver' + # Easy installation and use of chromedriver to run system tests with Chrome + gem 'chromedriver-helper' <%- end -%> end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 20f593f25c..160ad1a928 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -412,6 +412,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "Gemfile" do |content| assert_no_match(/capybara/, content) assert_no_match(/selenium-webdriver/, content) + assert_no_match(/chromedriver-helper/, content) end assert_no_directory("test") @@ -422,6 +423,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "Gemfile" do |content| assert_no_match(/capybara/, content) assert_no_match(/selenium-webdriver/, content) + assert_no_match(/chromedriver-helper/, content) end assert_directory("test") -- cgit v1.2.3 From 7e9ded512dd58a923a3be038843708dbba4215f6 Mon Sep 17 00:00:00 2001 From: Lisa Ugray Date: Thu, 12 Oct 2017 14:13:45 -0400 Subject: Start bringing attributes API to AM This is the first PR of a WIP to bring the attributes API to ActiveModel. It is not yet ready for public API. The `attributes_dirty_test.rb` file was created based on `dirty_test.rb`, and the simplifications in the diff do much to motivate this change. ``` diff activemodel/test/cases/dirty_test.rb activemodel/test/cases/attributes_dirty_test.rb 3a4 > require "active_model/attributes" 5c6 < class DirtyTest < ActiveModel::TestCase --- > class AttributesDirtyTest < ActiveModel::TestCase 7,41c8,12 < include ActiveModel::Dirty < define_attribute_methods :name, :color, :size < < def initialize < @name = nil < @color = nil < @size = nil < end < < def name < @name < end < < def name=(val) < name_will_change! < @name = val < end < < def color < @color < end < < def color=(val) < color_will_change! unless val == @color < @color = val < end < < def size < @size < end < < def size=(val) < attribute_will_change!(:size) unless val == @size < @size = val < end --- > include ActiveModel::Model > include ActiveModel::Attributes > attribute :name, :string > attribute :color, :string > attribute :size, :integer ``` --- activemodel/lib/active_model/attributes.rb | 89 +++++++++++ activemodel/test/cases/attributes_dirty_test.rb | 193 ++++++++++++++++++++++++ activemodel/test/cases/attributes_test.rb | 94 ++++++++++++ 3 files changed, 376 insertions(+) create mode 100644 activemodel/lib/active_model/attributes.rb create mode 100644 activemodel/test/cases/attributes_dirty_test.rb create mode 100644 activemodel/test/cases/attributes_test.rb diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb new file mode 100644 index 0000000000..3e34d3b83a --- /dev/null +++ b/activemodel/lib/active_model/attributes.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require "active_model/type" + +module ActiveModel + module Attributes #:nodoc: + extend ActiveSupport::Concern + include ActiveModel::AttributeMethods + include ActiveModel::Dirty + + included do + attribute_method_suffix "=" + class_attribute :attribute_types, :_default_attributes, instance_accessor: false + self.attribute_types = {} + self._default_attributes = {} + end + + module ClassMethods + def attribute(name, cast_type = Type::Value.new, **options) + self.attribute_types = attribute_types.merge(name.to_s => cast_type) + self._default_attributes = _default_attributes.merge(name.to_s => options[:default]) + define_attribute_methods(name) + end + + private + + def define_method_attribute=(name) + safe_name = name.unpack("h*".freeze).first + ActiveModel::AttributeMethods::AttrNames.set_name_cache safe_name, name + + generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 + def __temp__#{safe_name}=(value) + name = ::ActiveModel::AttributeMethods::AttrNames::ATTR_#{safe_name} + write_attribute(name, value) + end + alias_method #{(name + '=').inspect}, :__temp__#{safe_name}= + undef_method :__temp__#{safe_name}= + STR + end + end + + def initialize(*) + super + clear_changes_information + end + + private + + def write_attribute(attr_name, value) + name = if self.class.attribute_alias?(attr_name) + self.class.attribute_alias(attr_name).to_s + else + attr_name.to_s + end + + cast_type = self.class.attribute_types[name] + + deserialized_value = ActiveModel::Type.lookup(cast_type).cast(value) + attribute_will_change!(name) unless deserialized_value == attribute(name) + instance_variable_set("@#{name}", deserialized_value) + deserialized_value + end + + def attribute(name) + if instance_variable_defined?("@#{name}") + instance_variable_get("@#{name}") + else + default = self.class._default_attributes[name] + default.respond_to?(:call) ? default.call : default + end + end + + # Handle *= for method_missing. + def attribute=(attribute_name, value) + write_attribute(attribute_name, value) + end + end + + module AttributeMethods #:nodoc: + AttrNames = Module.new { + def self.set_name_cache(name, value) + const_name = "ATTR_#{name}" + unless const_defined? const_name + const_set const_name, value.dup.freeze + end + end + } + end +end diff --git a/activemodel/test/cases/attributes_dirty_test.rb b/activemodel/test/cases/attributes_dirty_test.rb new file mode 100644 index 0000000000..26b0e85db3 --- /dev/null +++ b/activemodel/test/cases/attributes_dirty_test.rb @@ -0,0 +1,193 @@ +# frozen_string_literal: true + +require "cases/helper" +require "active_model/attributes" + +class AttributesDirtyTest < ActiveModel::TestCase + class DirtyModel + include ActiveModel::Model + include ActiveModel::Attributes + attribute :name, :string + attribute :color, :string + attribute :size, :integer + + def save + changes_applied + end + + def reload + clear_changes_information + end + end + + setup do + @model = DirtyModel.new + end + + test "setting attribute will result in change" do + assert !@model.changed? + assert !@model.name_changed? + @model.name = "Ringo" + assert @model.changed? + assert @model.name_changed? + end + + test "list of changed attribute keys" do + assert_equal [], @model.changed + @model.name = "Paul" + assert_equal ["name"], @model.changed + end + + test "changes to attribute values" do + assert !@model.changes["name"] + @model.name = "John" + assert_equal [nil, "John"], @model.changes["name"] + end + + test "checking if an attribute has changed to a particular value" do + @model.name = "Ringo" + assert @model.name_changed?(from: nil, to: "Ringo") + assert_not @model.name_changed?(from: "Pete", to: "Ringo") + assert @model.name_changed?(to: "Ringo") + assert_not @model.name_changed?(to: "Pete") + assert @model.name_changed?(from: nil) + assert_not @model.name_changed?(from: "Pete") + end + + test "changes accessible through both strings and symbols" do + @model.name = "David" + assert_not_nil @model.changes[:name] + assert_not_nil @model.changes["name"] + end + + test "be consistent with symbols arguments after the changes are applied" do + @model.name = "David" + assert @model.attribute_changed?(:name) + @model.save + @model.name = "Rafael" + assert @model.attribute_changed?(:name) + end + + test "attribute mutation" do + @model.instance_variable_set("@name", "Yam".dup) + assert !@model.name_changed? + @model.name.replace("Hadad") + assert !@model.name_changed? + @model.name_will_change! + @model.name.replace("Baal") + assert @model.name_changed? + end + + test "resetting attribute" do + @model.name = "Bob" + @model.restore_name! + assert_nil @model.name + assert !@model.name_changed? + end + + test "setting color to same value should not result in change being recorded" do + @model.color = "red" + assert @model.color_changed? + @model.save + assert !@model.color_changed? + assert !@model.changed? + @model.color = "red" + assert !@model.color_changed? + assert !@model.changed? + end + + test "saving should reset model's changed status" do + @model.name = "Alf" + assert @model.changed? + @model.save + assert !@model.changed? + assert !@model.name_changed? + end + + test "saving should preserve previous changes" do + @model.name = "Jericho Cane" + @model.save + assert_equal [nil, "Jericho Cane"], @model.previous_changes["name"] + end + + test "setting new attributes should not affect previous changes" do + @model.name = "Jericho Cane" + @model.save + @model.name = "DudeFella ManGuy" + assert_equal [nil, "Jericho Cane"], @model.name_previous_change + end + + test "saving should preserve model's previous changed status" do + @model.name = "Jericho Cane" + @model.save + assert @model.name_previously_changed? + end + + test "previous value is preserved when changed after save" do + assert_equal({}, @model.changed_attributes) + @model.name = "Paul" + assert_equal({ "name" => nil }, @model.changed_attributes) + + @model.save + + @model.name = "John" + assert_equal({ "name" => "Paul" }, @model.changed_attributes) + end + + test "changing the same attribute multiple times retains the correct original value" do + @model.name = "Otto" + @model.save + @model.name = "DudeFella ManGuy" + @model.name = "Mr. Manfredgensonton" + assert_equal ["Otto", "Mr. Manfredgensonton"], @model.name_change + assert_equal @model.name_was, "Otto" + end + + test "using attribute_will_change! with a symbol" do + @model.size = 1 + assert @model.size_changed? + end + + test "reload should reset all changes" do + @model.name = "Dmitry" + @model.name_changed? + @model.save + @model.name = "Bob" + + assert_equal [nil, "Dmitry"], @model.previous_changes["name"] + assert_equal "Dmitry", @model.changed_attributes["name"] + + @model.reload + + assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.previous_changes + assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.changed_attributes + end + + test "restore_attributes should restore all previous data" do + @model.name = "Dmitry" + @model.color = "Red" + @model.save + @model.name = "Bob" + @model.color = "White" + + @model.restore_attributes + + assert_not @model.changed? + assert_equal "Dmitry", @model.name + assert_equal "Red", @model.color + end + + test "restore_attributes can restore only some attributes" do + @model.name = "Dmitry" + @model.color = "Red" + @model.save + @model.name = "Bob" + @model.color = "White" + + @model.restore_attributes(["name"]) + + assert @model.changed? + assert_equal "Dmitry", @model.name + assert_equal "White", @model.color + end +end diff --git a/activemodel/test/cases/attributes_test.rb b/activemodel/test/cases/attributes_test.rb new file mode 100644 index 0000000000..064cba40e3 --- /dev/null +++ b/activemodel/test/cases/attributes_test.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require "cases/helper" +require "active_model/attributes" + +module ActiveModel + class AttributesTest < ActiveModel::TestCase + class ModelForAttributesTest + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :integer_field, :integer + attribute :string_field, :string + attribute :decimal_field, :decimal + attribute :string_with_default, :string, default: "default string" + attribute :date_field, :string, default: -> { Date.new(2016, 1, 1) } + attribute :boolean_field, :boolean + end + + class ChildModelForAttributesTest < ModelForAttributesTest + end + + class GrandchildModelForAttributesTest < ChildModelForAttributesTest + attribute :integer_field, :string + end + + test "properties assignment" do + data = ModelForAttributesTest.new( + integer_field: "2.3", + string_field: "Rails FTW", + decimal_field: "12.3", + boolean_field: "0" + ) + + assert_equal 2, data.integer_field + assert_equal "Rails FTW", data.string_field + assert_equal BigDecimal.new("12.3"), data.decimal_field + assert_equal "default string", data.string_with_default + assert_equal Date.new(2016, 1, 1), data.date_field + assert_equal false, data.boolean_field + + data.integer_field = 10 + data.string_with_default = nil + data.boolean_field = "1" + + assert_equal 10, data.integer_field + assert_nil data.string_with_default + assert_equal true, data.boolean_field + end + + test "dirty" do + data = ModelForAttributesTest.new( + integer_field: "2.3", + string_field: "Rails FTW", + decimal_field: "12.3", + boolean_field: "0" + ) + + assert_equal false, data.changed? + + data.integer_field = "2.1" + + assert_equal false, data.changed? + + data.string_with_default = "default string" + + assert_equal false, data.changed? + + data.integer_field = "5.1" + + assert_equal true, data.changed? + assert_equal true, data.integer_field_changed? + assert_equal({ "integer_field" => [2, 5] }, data.changes) + end + + test "nonexistent attribute" do + assert_raise ActiveModel::UnknownAttributeError do + ModelForAttributesTest.new(nonexistent: "nonexistent") + end + end + + test "children inherit attributes" do + data = ChildModelForAttributesTest.new(integer_field: "4.4") + + assert_equal 4, data.integer_field + end + + test "children can override parents" do + data = GrandchildModelForAttributesTest.new(integer_field: "4.4") + + assert_equal "4.4", data.integer_field + end + end +end -- cgit v1.2.3 From 0e47e58a96c2cd6d7f655d0ccf35737ac5fbf80c Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 19 Oct 2017 08:29:48 +0900 Subject: Remove unused `FixtureTemplate` class `FixtureTemplate` is no longer used since 3d7892d. --- actionview/test/template/digestor_test.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb index 928b1ac7dd..1bfa39a319 100644 --- a/actionview/test/template/digestor_test.rb +++ b/actionview/test/template/digestor_test.rb @@ -4,17 +4,6 @@ require "abstract_unit" require "fileutils" require "action_view/dependency_tracker" -class FixtureTemplate - attr_reader :source, :handler - - def initialize(template_path) - @source = File.read(template_path) - @handler = ActionView::Template.handler_for_extension(:erb) - rescue Errno::ENOENT - raise ActionView::MissingTemplate.new([], "", [], true, []) - end -end - class FixtureFinder < ActionView::LookupContext FIXTURES_DIR = File.expand_path("../fixtures/digestor", __dir__) -- cgit v1.2.3 From 207c37a1ec6cdbe9865d622de9bf0c7e2599bdcc Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 19 Oct 2017 21:07:56 +0900 Subject: Test `ignored_columns` value is inheritable by subclasses --- activerecord/test/cases/base_test.rb | 7 +++++++ activerecord/test/models/developer.rb | 3 +++ 2 files changed, 10 insertions(+) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 1a1d4ce039..79a1982b25 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1444,17 +1444,24 @@ class BasicsTest < ActiveRecord::TestCase cache_columns = Developer.connection.schema_cache.columns_hash(Developer.table_name) assert_includes cache_columns.keys, "first_name" assert_not_includes Developer.columns_hash.keys, "first_name" + assert_not_includes SubDeveloper.columns_hash.keys, "first_name" end test "ignored columns have no attribute methods" do refute Developer.new.respond_to?(:first_name) refute Developer.new.respond_to?(:first_name=) refute Developer.new.respond_to?(:first_name?) + refute SubDeveloper.new.respond_to?(:first_name) + refute SubDeveloper.new.respond_to?(:first_name=) + refute SubDeveloper.new.respond_to?(:first_name?) end test "ignored columns don't prevent explicit declaration of attribute methods" do assert Developer.new.respond_to?(:last_name) assert Developer.new.respond_to?(:last_name=) assert Developer.new.respond_to?(:last_name?) + assert SubDeveloper.new.respond_to?(:last_name) + assert SubDeveloper.new.respond_to?(:last_name=) + assert SubDeveloper.new.respond_to?(:last_name?) end end diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 56aafca60b..2a86fe9eba 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -87,6 +87,9 @@ class Developer < ActiveRecord::Base private :track_instance_count end +class SubDeveloper < Developer +end + class AuditLog < ActiveRecord::Base belongs_to :developer, validate: true belongs_to :unvalidated_developer, class_name: "Developer" -- cgit v1.2.3 From 4dcf12a340f82fb01453ef3b7aca6df756bdaa65 Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Thu, 19 Oct 2017 11:01:52 -0500 Subject: PhantomJS is abandoned, replace it with Selenium/Chrome headless --- .travis.yml | 2 +- Gemfile | 5 ++ Gemfile.lock | 11 ++++ actionview/Rakefile | 2 +- ci/phantomjs.js | 149 -------------------------------------------- ci/qunit-selenium-runner.rb | 13 ++++ 6 files changed, 31 insertions(+), 151 deletions(-) delete mode 100644 ci/phantomjs.js create mode 100644 ci/qunit-selenium-runner.rb diff --git a/.travis.yml b/.travis.yml index 19164129c5..851365acbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ services: addons: postgresql: "9.6" + chrome: stable apt: sources: - sourceline: "ppa:mc3man/trusty-media" @@ -34,7 +35,6 @@ before_install: - "[[ $GEM != 'av:ujs' ]] || nvm install node" - "[[ $GEM != 'av:ujs' ]] || node --version" - "[[ $GEM != 'av:ujs' ]] || (cd actionview && npm install)" - - "[[ $GEM != 'av:ujs' ]] || [[ $(phantomjs --version) > '2' ]] || npm install -g phantomjs-prebuilt" before_script: # Set Sauce Labs username and access key. Obfuscated, purposefully not encrypted. diff --git a/Gemfile b/Gemfile index f545dd5fa1..38125cf0cc 100644 --- a/Gemfile +++ b/Gemfile @@ -102,6 +102,11 @@ group :storage do gem "mini_magick" end +group :ujs do + gem "qunit-selenium" + gem "chromedriver-helper" +end + # Add your own local bundler stuff. local_gemfile = File.expand_path(".Gemfile", __dir__) instance_eval File.read local_gemfile if File.exist? local_gemfile diff --git a/Gemfile.lock b/Gemfile.lock index ac56bca4ce..0889d69d3f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -136,6 +136,8 @@ GEM addressable (2.5.1) public_suffix (~> 2.0, >= 2.0.2) amq-protocol (2.2.0) + archive-zip (0.7.0) + io-like (~> 0.3.0) ast (2.3.0) aws-partitions (1.20.0) aws-sdk-core (3.3.0) @@ -199,6 +201,9 @@ GEM xpath (~> 2.0) childprocess (0.7.1) ffi (~> 1.0, >= 1.0.11) + chromedriver-helper (1.1.0) + archive-zip (~> 0.7.0) + nokogiri (~> 1.6) coffee-rails (4.2.2) coffee-script (>= 2.2.0) railties (>= 4.0.0) @@ -289,6 +294,7 @@ GEM http_parser.rb (0.6.0) httpclient (2.8.3) i18n (0.8.6) + io-like (0.3.0) jmespath (1.3.1) json (2.1.0) jwt (1.5.6) @@ -356,6 +362,9 @@ GEM public_suffix (2.0.5) puma (3.9.1) que (0.14.0) + qunit-selenium (0.0.4) + selenium-webdriver + thor racc (1.4.14) rack (2.0.3) rack-cache (1.7.0) @@ -498,6 +507,7 @@ DEPENDENCIES bootsnap (>= 1.1.0) byebug capybara (~> 2.15) + chromedriver-helper coffee-rails dalli (>= 2.2.1) delayed_job @@ -520,6 +530,7 @@ DEPENDENCIES puma que queue_classic! + qunit-selenium racc (>= 1.4.6) rack-cache (~> 1.2) rails! diff --git a/actionview/Rakefile b/actionview/Rakefile index 20dfa4e114..5f1c055692 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -45,7 +45,7 @@ namespace :test do end end - system("npm run lint && phantomjs ../ci/phantomjs.js http://localhost:4567/") + system("npm run lint && bundle exec ruby ../ci/qunit-selenium-runner.rb http://localhost:4567/") status = $?.to_i ensure Process.kill("KILL", pid) if pid diff --git a/ci/phantomjs.js b/ci/phantomjs.js deleted file mode 100644 index 7a33fb14a3..0000000000 --- a/ci/phantomjs.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * PhantomJS Runner QUnit Plugin 1.2.0 - * - * PhantomJS binaries: http://phantomjs.org/download.html - * Requires PhantomJS 1.6+ (1.7+ recommended) - * - * Run with: - * phantomjs runner.js [url-of-your-qunit-testsuite] - * - * e.g. - * phantomjs runner.js http://localhost/qunit/test/index.html - */ - -/*global phantom:false, require:false, console:false, window:false, QUnit:false */ - -(function() { - 'use strict'; - - var url, page, timeout, - args = require('system').args; - - // arg[0]: scriptName, args[1...]: arguments - if (args.length < 2 || args.length > 3) { - console.error('Usage:\n phantomjs runner.js [url-of-your-qunit-testsuite] [timeout-in-seconds]'); - phantom.exit(1); - } - - url = args[1]; - page = require('webpage').create(); - if (args[2] !== undefined) { - timeout = parseInt(args[2], 10); - } - - // Route `console.log()` calls from within the Page context to the main Phantom context (i.e. current `this`) - page.onConsoleMessage = function(msg) { - console.log(msg); - }; - - page.onInitialized = function() { - page.evaluate(addLogging); - }; - - page.onCallback = function(message) { - var result, - failed; - - if (message) { - if (message.name === 'QUnit.done') { - result = message.data; - failed = !result || !result.total || result.failed; - - if (!result.total) { - console.error('No tests were executed. Are you loading tests asynchronously?'); - } - - phantom.exit(failed ? 1 : 0); - } - } - }; - - page.open(url, function(status) { - if (status !== 'success') { - console.error('Unable to access network: ' + status); - phantom.exit(1); - } else { - // Cannot do this verification with the 'DOMContentLoaded' handler because it - // will be too late to attach it if a page does not have any script tags. - var qunitMissing = page.evaluate(function() { return (typeof QUnit === 'undefined' || !QUnit); }); - if (qunitMissing) { - console.error('The `QUnit` object is not present on this page.'); - phantom.exit(1); - } - - // Set a timeout on the test running, otherwise tests with async problems will hang forever - if (typeof timeout === 'number') { - setTimeout(function() { - console.error('The specified timeout of ' + timeout + ' seconds has expired. Aborting...'); - phantom.exit(1); - }, timeout * 1000); - } - - // Do nothing... the callback mechanism will handle everything! - } - }); - - function addLogging() { - window.document.addEventListener('DOMContentLoaded', function() { - var currentTestAssertions = []; - - QUnit.log(function(details) { - var response; - - // Ignore passing assertions - if (details.result) { - return; - } - - response = details.message || ''; - - if (typeof details.expected !== 'undefined') { - if (response) { - response += ', '; - } - - response += 'expected: ' + details.expected + ', but was: ' + details.actual; - } - - if (details.source) { - response += "\n" + details.source; - } - - currentTestAssertions.push('Failed assertion: ' + response); - }); - - QUnit.testDone(function(result) { - var i, - len, - name = ''; - - if (result.module) { - name += result.module + ': '; - } - name += result.name; - - if (result.failed) { - console.log('\n' + 'Test failed: ' + name); - - for (i = 0, len = currentTestAssertions.length; i < len; i++) { - console.log(' ' + currentTestAssertions[i]); - } - } - - currentTestAssertions.length = 0; - }); - - QUnit.done(function(result) { - console.log('\n' + 'Took ' + result.runtime + 'ms to run ' + result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' failed.'); - - if (typeof window.callPhantom === 'function') { - window.callPhantom({ - 'name': 'QUnit.done', - 'data': result - }); - } - }); - }, false); - } -})(); - diff --git a/ci/qunit-selenium-runner.rb b/ci/qunit-selenium-runner.rb new file mode 100644 index 0000000000..1d18d666b0 --- /dev/null +++ b/ci/qunit-selenium-runner.rb @@ -0,0 +1,13 @@ +require 'qunit/selenium/test_runner' +require 'chromedriver/helper' + +driver_options = Selenium::WebDriver::Chrome::Options.new +driver_options.add_argument('--headless') +driver_options.add_argument('--disable-gpu') + +driver = ::Selenium::WebDriver.for(:chrome, options: driver_options) +result = QUnit::Selenium::TestRunner.new(driver).open(ARGV[0], timeout: 60) +driver.quit + +puts "Time: #{result.duration} seconds, Total: #{result.tests[:total]}, Passed: #{result.tests[:passed]}, Failed: #{result.tests[:failed]}" +exit(result.tests[:failed] > 0 ? 1 : 0) -- cgit v1.2.3 From 734f98178261e2ba42df3ff6c39c8950e9eb01ed Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Thu, 19 Oct 2017 11:11:20 -0500 Subject: Count assertions instead of tests in report --- ci/qunit-selenium-runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/qunit-selenium-runner.rb b/ci/qunit-selenium-runner.rb index 1d18d666b0..9b856e2a41 100644 --- a/ci/qunit-selenium-runner.rb +++ b/ci/qunit-selenium-runner.rb @@ -9,5 +9,5 @@ driver = ::Selenium::WebDriver.for(:chrome, options: driver_options) result = QUnit::Selenium::TestRunner.new(driver).open(ARGV[0], timeout: 60) driver.quit -puts "Time: #{result.duration} seconds, Total: #{result.tests[:total]}, Passed: #{result.tests[:passed]}, Failed: #{result.tests[:failed]}" +puts "Time: #{result.duration} seconds, Total: #{result.assertions[:total]}, Passed: #{result.assertions[:passed]}, Failed: #{result.assertions[:failed]}" exit(result.tests[:failed] > 0 ? 1 : 0) -- cgit v1.2.3 From a52ff1b0848e8bb2310738ec7b762b82edbbfcb2 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 20 Oct 2017 05:34:47 +0900 Subject: Keep `:api: plugin` methods in the doc [ci skip] `:api:` tag was removed in 5349f231 since RDoc doesn't support `:api:` tag. But those methods are not private API, they are public API for renderers. The renderers should be able to know that they can override this method. --- actionpack/lib/abstract_controller/rendering.rb | 6 +++--- actionpack/lib/action_controller/metal/instrumentation.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 7e156586b9..fe1cd4a5a5 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -75,7 +75,7 @@ module AbstractController # Normalize args by converting render "foo" to # render :action => "foo" and render "foo/bar" to # render :file => "foo/bar". - def _normalize_args(action = nil, options = {}) + def _normalize_args(action = nil, options = {}) # :doc: if action.respond_to?(:permitted?) if action.permitted? action @@ -90,12 +90,12 @@ module AbstractController end # Normalize options. - def _normalize_options(options) + def _normalize_options(options) # :doc: options end # Process extra options. - def _process_options(options) + def _process_options(options) # :doc: options end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index 5ef83af07a..be9449629f 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -83,13 +83,13 @@ module ActionController # def cleanup_view_runtime # super - time_taken_in_something_expensive # end - def cleanup_view_runtime + def cleanup_view_runtime # :doc: yield end # Every time after an action is processed, this method is invoked # with the payload, so you can add more information. - def append_info_to_payload(payload) + def append_info_to_payload(payload) # :doc: payload[:view_runtime] = view_runtime end -- cgit v1.2.3 From 065be937f27c25bc7d23e0dd35ad4aa84818b167 Mon Sep 17 00:00:00 2001 From: Max Felsher Date: Thu, 19 Oct 2017 22:34:02 -0400 Subject: Remove obsolete documentation [ci skip] Instructions to use `h` or `html_escape` in ERB templates were added to `actionpack/lib/action_view/template_handlers/erb.rb` in a1b0349 (Rails 2.1), but ERB has automatically escaped values since Rails 3. --- activesupport/lib/active_support/core_ext/string/output_safety.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 600b41da10..873213e6d7 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -15,9 +15,6 @@ class ERB # A utility method for escaping HTML tag characters. # This method is also aliased as h. # - # In your ERB templates, use this method to escape any unsafe content. For example: - # <%= h @person.name %> - # # puts html_escape('is a > 0 & a < 10?') # # => is a > 0 & a < 10? def html_escape(s) -- cgit v1.2.3 From e0b11a241783ddf087ef383fa931b5b63c3a805e Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 20 Oct 2017 15:06:37 +0900 Subject: Return correct exit status of ujs test The `Process::Status#to_i` returns the bits in stat. If need exit status, need to use `#exitstatus`. Ref: https://ruby-doc.org/core-2.4.0/Process/Status.html#method-i-to_i --- actionview/Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionview/Rakefile b/actionview/Rakefile index 5f1c055692..9e5ef35334 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -46,7 +46,7 @@ namespace :test do end system("npm run lint && bundle exec ruby ../ci/qunit-selenium-runner.rb http://localhost:4567/") - status = $?.to_i + status = $?.exitstatus ensure Process.kill("KILL", pid) if pid FileUtils.rm_rf("log") -- cgit v1.2.3 From e9f82e76e83497db094fe63104f548453fbe512e Mon Sep 17 00:00:00 2001 From: Altech Date: Thu, 19 Oct 2017 16:30:31 +0900 Subject: Convert ignored_columns to a list of string --- activerecord/lib/active_record/model_schema.rb | 32 ++++++++++++++------------ activerecord/test/cases/base_test.rb | 12 ++++++++++ activerecord/test/models/developer.rb | 8 +++++++ 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 34c0ef4e75..bed9400f51 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -115,20 +115,6 @@ module ActiveRecord # If true, the default table name for a Product class will be "products". If false, it would just be "product". # See table_name for the full rules on table/class naming. This is true, by default. - ## - # :singleton-method: ignored_columns - # :call-seq: ignored_columns - # - # The list of columns names the model should ignore. Ignored columns won't have attribute - # accessors defined, and won't be referenced in SQL queries. - - ## - # :singleton-method: ignored_columns= - # :call-seq: ignored_columns=(columns) - # - # Sets the columns names the model should ignore. Ignored columns won't have attribute - # accessors defined, and won't be referenced in SQL queries. - included do mattr_accessor :primary_key_prefix_type, instance_writer: false @@ -138,9 +124,9 @@ module ActiveRecord class_attribute :internal_metadata_table_name, instance_accessor: false, default: "ar_internal_metadata" class_attribute :protected_environments, instance_accessor: false, default: [ "production" ] class_attribute :pluralize_table_names, instance_writer: false, default: true - class_attribute :ignored_columns, instance_accessor: false, default: [].freeze self.inheritance_column = "type" + self.ignored_columns = [].freeze delegate :type_for_attribute, to: :class @@ -271,6 +257,22 @@ module ActiveRecord @explicit_inheritance_column = true end + # The list of columns names the model should ignore. Ignored columns won't have attribute + # accessors defined, and won't be referenced in SQL queries. + def ignored_columns + if defined?(@ignored_columns) + @ignored_columns + else + superclass.ignored_columns + end + end + + # Sets the columns names the model should ignore. Ignored columns won't have attribute + # accessors defined, and won't be referenced in SQL queries. + def ignored_columns=(columns) + @ignored_columns = columns.map(&:to_s) + end + def sequence_name if base_class == self @sequence_name ||= reset_sequence_name diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 79a1982b25..f0ef522515 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1445,6 +1445,7 @@ class BasicsTest < ActiveRecord::TestCase assert_includes cache_columns.keys, "first_name" assert_not_includes Developer.columns_hash.keys, "first_name" assert_not_includes SubDeveloper.columns_hash.keys, "first_name" + assert_not_includes SymbolIgnoredDeveloper.columns_hash.keys, "first_name" end test "ignored columns have no attribute methods" do @@ -1454,6 +1455,9 @@ class BasicsTest < ActiveRecord::TestCase refute SubDeveloper.new.respond_to?(:first_name) refute SubDeveloper.new.respond_to?(:first_name=) refute SubDeveloper.new.respond_to?(:first_name?) + refute SymbolIgnoredDeveloper.new.respond_to?(:first_name) + refute SymbolIgnoredDeveloper.new.respond_to?(:first_name=) + refute SymbolIgnoredDeveloper.new.respond_to?(:first_name?) end test "ignored columns don't prevent explicit declaration of attribute methods" do @@ -1463,5 +1467,13 @@ class BasicsTest < ActiveRecord::TestCase assert SubDeveloper.new.respond_to?(:last_name) assert SubDeveloper.new.respond_to?(:last_name=) assert SubDeveloper.new.respond_to?(:last_name?) + assert SymbolIgnoredDeveloper.new.respond_to?(:last_name) + assert SymbolIgnoredDeveloper.new.respond_to?(:last_name=) + assert SymbolIgnoredDeveloper.new.respond_to?(:last_name?) + end + + test "ignored columns are stored as an array of string" do + assert_equal(%w(first_name last_name), Developer.ignored_columns) + assert_equal(%w(first_name last_name), SymbolIgnoredDeveloper.ignored_columns) end end diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 2a86fe9eba..8881c69368 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -90,6 +90,14 @@ end class SubDeveloper < Developer end +class SymbolIgnoredDeveloper < ActiveRecord::Base + self.table_name = "developers" + self.ignored_columns = [:first_name, :last_name] + + attr_accessor :last_name + define_attribute_method "last_name" +end + class AuditLog < ActiveRecord::Base belongs_to :developer, validate: true belongs_to :unvalidated_developer, class_name: "Developer" -- cgit v1.2.3 From feb0fd0ca91052bbdce8ed67ffa937ecfcf249ec Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 20 Oct 2017 20:34:43 +0900 Subject: bundle lock --add-platform java "The dependency activerecord-jdbcsqlite3-adapter (>= 1.3.0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby, x64-mingw32, x86-mingw32 but the dependency is only for java. To add those platforms to the bundle, run `bundle lock --add-platform java`." --- Gemfile.lock | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 0889d69d3f..6ac4af557e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,6 +133,17 @@ PATH GEM remote: https://rubygems.org/ specs: + activerecord-jdbc-adapter (1.3.24) + activerecord (>= 2.2, < 5.0) + activerecord-jdbcmysql-adapter (1.3.24) + activerecord-jdbc-adapter (~> 1.3.24) + jdbc-mysql (>= 5.1.22) + activerecord-jdbcpostgresql-adapter (1.3.24) + activerecord-jdbc-adapter (~> 1.3.24) + jdbc-postgres (~> 9.1, <= 9.4.1206) + activerecord-jdbcsqlite3-adapter (1.3.24) + activerecord-jdbc-adapter (~> 1.3.24) + jdbc-sqlite3 (>= 3.7.2, < 3.9) addressable (2.5.1) public_suffix (~> 2.0, >= 2.0.2) amq-protocol (2.2.0) @@ -165,6 +176,7 @@ GEM concurrent-ruby (~> 1.0.1) dante (> 0.1.5) bcrypt (3.1.11) + bcrypt (3.1.11-java) bcrypt (3.1.11-x64-mingw32) bcrypt (3.1.11-x86-mingw32) beaneater (1.0.0) @@ -188,6 +200,8 @@ GEM selenium-webdriver bootsnap (1.1.2) msgpack (~> 1.0) + bootsnap (1.1.2-java) + msgpack (~> 1.0) builder (3.2.3) bunny (2.6.6) amq-protocol (>= 2.1.0) @@ -212,6 +226,7 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.0.5) + concurrent-ruby (1.0.5-java) connection_pool (2.2.1) cookiejar (0.3.3) curses (1.0.2) @@ -243,6 +258,7 @@ GEM tzinfo event_emitter (0.2.6) eventmachine (1.2.5) + eventmachine (1.2.5-java) eventmachine (1.2.5-x64-mingw32) eventmachine (1.2.5-x86-mingw32) execjs (2.7.0) @@ -262,6 +278,7 @@ GEM eventmachine (>= 0.12.0) websocket-driver (>= 0.5.1) ffi (1.9.18) + ffi (1.9.18-java) ffi (1.9.18-x64-mingw32) ffi (1.9.18-x86-mingw32) globalid (0.4.0) @@ -291,12 +308,17 @@ GEM os (~> 0.9) signet (~> 0.7) hiredis (0.6.1) + hiredis (0.6.1-java) http_parser.rb (0.6.0) httpclient (2.8.3) i18n (0.8.6) io-like (0.3.0) + jdbc-mysql (5.1.44) + jdbc-postgres (9.4.1206) + jdbc-sqlite3 (3.8.11.2) jmespath (1.3.1) json (2.1.0) + json (2.1.0-java) jwt (1.5.6) kindlerb (1.2.0) mustache @@ -333,6 +355,7 @@ GEM metaclass (~> 0.0.1) mono_logger (1.1.0) msgpack (1.1.0) + msgpack (1.1.0-java) msgpack (1.1.0-x64-mingw32) msgpack (1.1.0-x86-mingw32) multi_json (1.12.1) @@ -345,6 +368,7 @@ GEM nio4r (2.1.0) nokogiri (1.8.0) mini_portile2 (~> 2.2.0) + nokogiri (1.8.0-java) nokogiri (1.8.0-x64-mingw32) mini_portile2 (~> 2.2.0) nokogiri (1.8.0-x86-mingw32) @@ -361,6 +385,7 @@ GEM psych (2.2.4) public_suffix (2.0.5) puma (3.9.1) + puma (3.9.1-java) que (0.14.0) qunit-selenium (0.0.4) selenium-webdriver @@ -461,6 +486,7 @@ GEM thor (0.20.0) thread (0.1.7) thread_safe (0.3.6) + thread_safe (0.3.6-java) tilt (2.0.8) turbolinks (5.0.1) turbolinks-source (~> 5) @@ -488,6 +514,7 @@ GEM nokogiri (~> 1.3) PLATFORMS + java ruby x64-mingw32 x86-mingw32 -- cgit v1.2.3 From 590e3e7ad3c7c8c9127c580b01885fffa90d3bb6 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 21 Oct 2017 05:37:53 +0900 Subject: Add test cases for `type` and `foreign_type` in the reflections It should be initialized only when polymorphic associations. --- activerecord/test/cases/reflection_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 4cd2d3aedc..de1d244d65 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -366,9 +366,16 @@ class ReflectionTest < ActiveRecord::TestCase assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.active_record_primary_key } end + def test_type + assert_equal "taggable_type", Post.reflect_on_association(:taggings).type.to_s + assert_equal "imageable_class", Post.reflect_on_association(:images).type.to_s + assert_nil Post.reflect_on_association(:readers).type + end + def test_foreign_type assert_equal "sponsorable_type", Sponsor.reflect_on_association(:sponsorable).foreign_type.to_s assert_equal "sponsorable_type", Sponsor.reflect_on_association(:thing).foreign_type.to_s + assert_nil Sponsor.reflect_on_association(:sponsor_club).foreign_type end def test_collection_association -- cgit v1.2.3 From a524c955bfcce118da0ecb21affe28e2a102a54b Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 15:22:36 +0900 Subject: Hash#slice is in Ruby 2.5+ since r60229 --- activesupport/lib/active_support/core_ext/hash/slice.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 94dc225edb..6a25e779e9 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -23,7 +23,7 @@ class Hash def slice(*keys) keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) } - end + end unless method_defined?(:slice) # Replaces the hash with only the given keys. # Returns a hash containing the removed key/value pairs. -- cgit v1.2.3 From d6f3b91aaa4af88eda2344629afed64640d51e0f Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 17:22:45 +0900 Subject: Move HWIA specific logic for slice and slice! to HWIA class --- activesupport/lib/active_support/core_ext/hash/slice.rb | 2 -- .../lib/active_support/hash_with_indifferent_access.rb | 10 ++++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 6a25e779e9..ed6cd9609a 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -21,7 +21,6 @@ class Hash # valid_keys = [:mass, :velocity, :time] # search(options.slice(*valid_keys)) def slice(*keys) - keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) } end unless method_defined?(:slice) @@ -31,7 +30,6 @@ class Hash # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b) # # => {:c=>3, :d=>4} def slice!(*keys) - keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) omit = slice(*self.keys - keys) hash = slice(*keys) hash.default = default diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index fcc13feb8c..f8bff4fe3e 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -311,6 +311,16 @@ module ActiveSupport dup.tap { |hash| hash.transform_keys!(*args, &block) } end + def slice(*keys) + keys.map! { |key| convert_key(key) } + self.class.new(super) + end + + def slice!(*keys) + keys.map! { |key| convert_key(key) } + super + end + def compact dup.tap(&:compact!) end -- cgit v1.2.3 From 01ae39660243bc5f0a986e20f9c9bff312b1b5f8 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 17:24:18 +0900 Subject: Let Hash#slice return a Hash In order to keep this method compatible with the Ruby 2.5 version of Hash#slice. This bahavior is actually slightly incompatibile with previous versions of Active Support but it might not cause a real problem, since HWIA, the biggest use case of Hash subclassing here, already overrides `slice` to return another HWIA. --- activesupport/CHANGELOG.md | 4 ++++ activesupport/lib/active_support/core_ext/hash/slice.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index c7924fa9ae..7696fdcd7a 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,7 @@ +* `Hash#slice` now falls back to Ruby 2.5+'s built-in definition if defined. + + *Akira Matsuda* + * Deprecate `secrets.secret_token`. The architecture for secrets had a big upgrade between Rails 3 and Rails 4, diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index ed6cd9609a..2bd0a56ea4 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -21,7 +21,7 @@ class Hash # valid_keys = [:mass, :velocity, :time] # search(options.slice(*valid_keys)) def slice(*keys) - keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) } + keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) } end unless method_defined?(:slice) # Replaces the hash with only the given keys. -- cgit v1.2.3 From 6a728491b66340345a91264b5983ad81944ab97a Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:08:33 +0900 Subject: [Railties] require_relative => require This basically reverts 618268b4b9382f4bcf004a945fe2d85c0bd03e32 --- railties/lib/rails.rb | 8 ++++---- railties/lib/rails/api/task.rb | 2 +- railties/lib/rails/app_loader.rb | 4 ++-- railties/lib/rails/application.rb | 6 +++--- railties/lib/rails/application/bootstrap.rb | 2 +- railties/lib/rails/application/configuration.rb | 4 ++-- railties/lib/rails/cli.rb | 6 +++--- railties/lib/rails/code_statistics.rb | 2 +- railties/lib/rails/command/base.rb | 2 +- railties/lib/rails/commands.rb | 2 +- railties/lib/rails/commands/application/application_command.rb | 4 ++-- railties/lib/rails/commands/console/console_command.rb | 2 +- railties/lib/rails/commands/credentials/credentials_command.rb | 8 ++++---- railties/lib/rails/commands/dbconsole/dbconsole_command.rb | 2 +- railties/lib/rails/commands/destroy/destroy_command.rb | 2 +- railties/lib/rails/commands/generate/generate_command.rb | 2 +- railties/lib/rails/commands/plugin/plugin_command.rb | 4 ++-- railties/lib/rails/commands/secrets/secrets_command.rb | 6 +++--- railties/lib/rails/commands/server/server_command.rb | 2 +- railties/lib/rails/commands/test/test_command.rb | 6 +++--- railties/lib/rails/configuration.rb | 4 ++-- railties/lib/rails/engine.rb | 10 +++++----- railties/lib/rails/engine/commands.rb | 2 +- railties/lib/rails/engine/configuration.rb | 2 +- railties/lib/rails/engine/updater.rb | 4 ++-- railties/lib/rails/generators.rb | 2 +- railties/lib/rails/generators/app_base.rb | 4 ++-- railties/lib/rails/generators/css/assets/assets_generator.rb | 2 +- .../lib/rails/generators/css/scaffold/scaffold_generator.rb | 2 +- railties/lib/rails/generators/erb.rb | 2 +- .../rails/generators/erb/controller/controller_generator.rb | 2 +- railties/lib/rails/generators/erb/mailer/mailer_generator.rb | 2 +- .../lib/rails/generators/erb/scaffold/scaffold_generator.rb | 4 ++-- railties/lib/rails/generators/js/assets/assets_generator.rb | 2 +- railties/lib/rails/generators/migration.rb | 2 +- railties/lib/rails/generators/model_helpers.rb | 2 +- railties/lib/rails/generators/named_base.rb | 4 ++-- railties/lib/rails/generators/rails/app/app_generator.rb | 8 ++++---- .../generators/rails/credentials/credentials_generator.rb | 4 ++-- .../rails/encrypted_secrets/encrypted_secrets_generator.rb | 4 ++-- .../rails/generators/rails/master_key/master_key_generator.rb | 2 +- railties/lib/rails/generators/rails/model/model_generator.rb | 2 +- railties/lib/rails/generators/rails/plugin/plugin_generator.rb | 2 +- .../lib/rails/generators/rails/resource/resource_generator.rb | 4 ++-- .../lib/rails/generators/rails/scaffold/scaffold_generator.rb | 2 +- .../rails/scaffold_controller/scaffold_controller_generator.rb | 2 +- railties/lib/rails/generators/resource_helpers.rb | 4 ++-- railties/lib/rails/generators/test_case.rb | 8 ++++---- railties/lib/rails/generators/test_unit.rb | 2 +- .../generators/test_unit/controller/controller_generator.rb | 2 +- .../generators/test_unit/generator/generator_generator.rb | 2 +- .../lib/rails/generators/test_unit/helper/helper_generator.rb | 2 +- .../generators/test_unit/integration/integration_generator.rb | 2 +- railties/lib/rails/generators/test_unit/job/job_generator.rb | 2 +- .../lib/rails/generators/test_unit/mailer/mailer_generator.rb | 2 +- .../lib/rails/generators/test_unit/model/model_generator.rb | 2 +- .../lib/rails/generators/test_unit/plugin/plugin_generator.rb | 2 +- .../rails/generators/test_unit/scaffold/scaffold_generator.rb | 4 ++-- .../lib/rails/generators/test_unit/system/system_generator.rb | 2 +- railties/lib/rails/generators/testing/behaviour.rb | 2 +- railties/lib/rails/info_controller.rb | 2 +- railties/lib/rails/mailers_controller.rb | 2 +- railties/lib/rails/plugin/test.rb | 4 ++-- railties/lib/rails/railtie.rb | 2 +- railties/lib/rails/railtie/configuration.rb | 2 +- railties/lib/rails/tasks/annotations.rake | 2 +- railties/lib/rails/tasks/dev.rake | 2 +- railties/lib/rails/tasks/engine.rake | 2 +- railties/lib/rails/tasks/framework.rake | 6 +++--- railties/lib/rails/tasks/statistics.rake | 2 +- railties/lib/rails/test_help.rb | 2 +- railties/lib/rails/test_unit/railtie.rb | 2 +- railties/lib/rails/test_unit/testing.rake | 2 +- railties/lib/rails/welcome_controller.rb | 2 +- 74 files changed, 115 insertions(+), 115 deletions(-) diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index 04bb9ba94a..092105d502 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "rails/ruby_version_check" +require "rails/ruby_version_check" require "pathname" @@ -11,8 +11,8 @@ require "active_support/core_ext/module/delegation" require "active_support/core_ext/array/extract_options" require "active_support/core_ext/object/blank" -require_relative "rails/application" -require_relative "rails/version" +require "rails/application" +require "rails/version" require "active_support/railtie" require "action_dispatch/railtie" @@ -50,7 +50,7 @@ module Rails def backtrace_cleaner @backtrace_cleaner ||= begin # Relies on Active Support, so we have to lazy load to postpone definition until Active Support has been loaded - require_relative "rails/backtrace_cleaner" + require "rails/backtrace_cleaner" Rails::BacktraceCleaner.new end end diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb index 184f5b14f1..e7f0557584 100644 --- a/railties/lib/rails/api/task.rb +++ b/railties/lib/rails/api/task.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "rdoc/task" -require_relative "generator" +require "rails/api/generator" module Rails module API diff --git a/railties/lib/rails/app_loader.rb b/railties/lib/rails/app_loader.rb index 3e9b3bd4bb..20eb75d95c 100644 --- a/railties/lib/rails/app_loader.rb +++ b/railties/lib/rails/app_loader.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "pathname" -require_relative "version" +require "rails/version" module Rails module AppLoader # :nodoc: @@ -56,7 +56,7 @@ EOS $stderr.puts(BUNDLER_WARNING) Object.const_set(:APP_PATH, File.expand_path("config/application", Dir.pwd)) require File.expand_path("../boot", APP_PATH) - require_relative "commands" + require "rails/commands" break end end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 4fd20185b1..ade8cb6a48 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -7,8 +7,8 @@ require "active_support/key_generator" require "active_support/message_verifier" require "active_support/encrypted_configuration" require "active_support/deprecation" -require_relative "engine" -require_relative "secrets" +require "rails/engine" +require "rails/secrets" module Rails # An Engine with the responsibility of coordinating the whole boot process. @@ -474,7 +474,7 @@ module Rails def run_tasks_blocks(app) #:nodoc: railties.each { |r| r.run_tasks_blocks(app) } super - require_relative "tasks" + require "rails/tasks" task :environment do ActiveSupport.on_load(:before_initialize) { config.eager_load = false } diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index c24d4573a9..e3c0759f95 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -4,7 +4,7 @@ require "fileutils" require "active_support/notifications" require "active_support/dependencies" require "active_support/descendants_tracker" -require_relative "../secrets" +require "rails/secrets" module Rails class Application diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index b65289177f..290ec13878 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -2,8 +2,8 @@ require "active_support/core_ext/kernel/reporting" require "active_support/file_update_checker" -require_relative "../engine/configuration" -require_relative "../source_annotation_extractor" +require "rails/engine/configuration" +require "rails/source_annotation_extractor" module Rails class Application diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb index 50a2ed30cf..e56e604fdc 100644 --- a/railties/lib/rails/cli.rb +++ b/railties/lib/rails/cli.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require_relative "app_loader" +require "rails/app_loader" # If we are inside a Rails application this method performs an exec and thus # the rest of this script is not run. Rails::AppLoader.exec_app -require_relative "ruby_version_check" +require "rails/ruby_version_check" Signal.trap("INT") { puts; exit(1) } -require_relative "command" +require "rails/command" if ARGV.first == "plugin" ARGV.shift diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb index 7ceb86198f..9c447c366f 100644 --- a/railties/lib/rails/code_statistics.rb +++ b/railties/lib/rails/code_statistics.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "code_statistics_calculator" +require "rails/code_statistics_calculator" require "active_support/core_ext/enumerable" class CodeStatistics #:nodoc: diff --git a/railties/lib/rails/command/base.rb b/railties/lib/rails/command/base.rb index 8df4f98d3e..fa462ef7e9 100644 --- a/railties/lib/rails/command/base.rb +++ b/railties/lib/rails/command/base.rb @@ -6,7 +6,7 @@ require "erb" require "active_support/core_ext/string/filters" require "active_support/core_ext/string/inflections" -require_relative "actions" +require "rails/command/actions" module Rails module Command diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb index 1aea1e1a96..77961a0292 100644 --- a/railties/lib/rails/commands.rb +++ b/railties/lib/rails/commands.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "command" +require "rails/command" aliases = { "g" => "generate", diff --git a/railties/lib/rails/commands/application/application_command.rb b/railties/lib/rails/commands/application/application_command.rb index 13d47a63bc..f77553b830 100644 --- a/railties/lib/rails/commands/application/application_command.rb +++ b/railties/lib/rails/commands/application/application_command.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../generators" -require_relative "../../generators/rails/app/app_generator" +require "rails/generators" +require "rails/generators/rails/app/app_generator" module Rails module Generators diff --git a/railties/lib/rails/commands/console/console_command.rb b/railties/lib/rails/commands/console/console_command.rb index 5dc695c240..e35faa5b01 100644 --- a/railties/lib/rails/commands/console/console_command.rb +++ b/railties/lib/rails/commands/console/console_command.rb @@ -3,7 +3,7 @@ require "irb" require "irb/completion" -require_relative "../../command/environment_argument" +require "rails/command/environment_argument" module Rails class Console diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb index 88fb032d84..1ef7c1f343 100644 --- a/railties/lib/rails/commands/credentials/credentials_command.rb +++ b/railties/lib/rails/commands/credentials/credentials_command.rb @@ -67,15 +67,15 @@ module Rails def master_key_generator - require_relative "../../generators" - require_relative "../../generators/rails/master_key/master_key_generator" + require "rails/generators" + require "rails/generators/rails/master_key/master_key_generator" Rails::Generators::MasterKeyGenerator.new end def credentials_generator - require_relative "../../generators" - require_relative "../../generators/rails/credentials/credentials_generator" + require "rails/generators" + require "rails/generators/rails/credentials/credentials_generator" Rails::Generators::CredentialsGenerator.new end diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb index 5234969743..8df548b5de 100644 --- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb +++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../command/environment_argument" +require "rails/command/environment_argument" module Rails class DBConsole diff --git a/railties/lib/rails/commands/destroy/destroy_command.rb b/railties/lib/rails/commands/destroy/destroy_command.rb index 686193ddb9..dd432d28fd 100644 --- a/railties/lib/rails/commands/destroy/destroy_command.rb +++ b/railties/lib/rails/commands/destroy/destroy_command.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../generators" +require "rails/generators" module Rails module Command diff --git a/railties/lib/rails/commands/generate/generate_command.rb b/railties/lib/rails/commands/generate/generate_command.rb index 73f627637d..93d7a0ce3a 100644 --- a/railties/lib/rails/commands/generate/generate_command.rb +++ b/railties/lib/rails/commands/generate/generate_command.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../generators" +require "rails/generators" module Rails module Command diff --git a/railties/lib/rails/commands/plugin/plugin_command.rb b/railties/lib/rails/commands/plugin/plugin_command.rb index 5d3dfadf84..2b192abf9b 100644 --- a/railties/lib/rails/commands/plugin/plugin_command.rb +++ b/railties/lib/rails/commands/plugin/plugin_command.rb @@ -36,8 +36,8 @@ module Rails private def run_plugin_generator(plugin_args) - require_relative "../../generators" - require_relative "../../generators/rails/plugin/plugin_generator" + require "rails/generators" + require "rails/generators/rails/plugin/plugin_generator" Rails::Generators::PluginGenerator.start plugin_args end end diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb index d93c4de74e..c91139e33b 100644 --- a/railties/lib/rails/commands/secrets/secrets_command.rb +++ b/railties/lib/rails/commands/secrets/secrets_command.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support" -require_relative "../../secrets" +require "rails/secrets" module Rails module Command @@ -56,8 +56,8 @@ module Rails private def generator - require_relative "../../generators" - require_relative "../../generators/rails/encrypted_secrets/encrypted_secrets_generator" + require "rails/generators" + require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" Rails::Generators::EncryptedSecretsGenerator end diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index 5b5037d3de..703ec59087 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -6,7 +6,7 @@ require "action_dispatch" require "rails" require "active_support/deprecation" require "active_support/core_ext/string/filters" -require_relative "../../dev_caching" +require "rails/dev_caching" module Rails class Server < ::Rack::Server diff --git a/railties/lib/rails/commands/test/test_command.rb b/railties/lib/rails/commands/test/test_command.rb index a2216553ca..00ea9ac4a6 100644 --- a/railties/lib/rails/commands/test/test_command.rb +++ b/railties/lib/rails/commands/test/test_command.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../../command" -require_relative "../../test_unit/runner" -require_relative "../../test_unit/reporter" +require "rails/command" +require "rails/test_unit/runner" +require "rails/test_unit/reporter" module Rails module Command diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 70815d114d..d3a54d9364 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -2,8 +2,8 @@ require "active_support/ordered_options" require "active_support/core_ext/object" -require_relative "paths" -require_relative "rack" +require "rails/paths" +require "rails/rack" module Rails module Configuration diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index cc2030d37d..6a13a84108 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "railtie" -require_relative "engine/railties" +require "rails/railtie" +require "rails/engine/railties" require "active_support/core_ext/module/delegation" require "pathname" require "thread" @@ -439,8 +439,8 @@ module Rails # Load console and invoke the registered hooks. # Check Rails::Railtie.console for more info. def load_console(app = self) - require_relative "console/app" - require_relative "console/helpers" + require "rails/console/app" + require "rails/console/helpers" run_console_blocks(app) self end @@ -463,7 +463,7 @@ module Rails # Load Rails generators and invoke the registered hooks. # Check Rails::Railtie.generators for more info. def load_generators(app = self) - require_relative "generators" + require "rails/generators" run_generators_blocks(app) Rails::Generators.configure!(app.config.generators) self diff --git a/railties/lib/rails/engine/commands.rb b/railties/lib/rails/engine/commands.rb index 3854907507..05218640c6 100644 --- a/railties/lib/rails/engine/commands.rb +++ b/railties/lib/rails/engine/commands.rb @@ -6,4 +6,4 @@ unless defined?(APP_PATH) end end -require_relative "../commands" +require "rails/commands" diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index 16ba7f9eb8..6bf0406b21 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../railtie/configuration" +require "rails/railtie/configuration" module Rails class Engine diff --git a/railties/lib/rails/engine/updater.rb b/railties/lib/rails/engine/updater.rb index 21a0fc5562..be7a47124a 100644 --- a/railties/lib/rails/engine/updater.rb +++ b/railties/lib/rails/engine/updater.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../generators" -require_relative "../generators/rails/plugin/plugin_generator" +require "rails/generators" +require "rails/generators/rails/plugin/plugin_generator" module Rails class Engine diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index a630d55e59..2d265818f7 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -4,7 +4,7 @@ activesupport_path = File.expand_path("../../../activesupport/lib", __dir__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) require "thor/group" -require_relative "command" +require "rails/command" require "active_support" require "active_support/core_ext/object/blank" diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 6fb4ea52b3..bc00adabae 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -3,10 +3,10 @@ require "fileutils" require "digest/md5" require "active_support/core_ext/string/strip" -require_relative "../version" unless defined?(Rails::VERSION) +require "rails/version" unless defined?(Rails::VERSION) require "open-uri" require "uri" -require_relative "../generators" +require "rails/generators" require "active_support/core_ext/array/extract_options" module Rails diff --git a/railties/lib/rails/generators/css/assets/assets_generator.rb b/railties/lib/rails/generators/css/assets/assets_generator.rb index 5f7be769b2..f657d1e50f 100644 --- a/railties/lib/rails/generators/css/assets/assets_generator.rb +++ b/railties/lib/rails/generators/css/assets/assets_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../named_base" +require "rails/generators/named_base" module Css # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb index d8eb4f2c7b..89c560f382 100644 --- a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../named_base" +require "rails/generators/named_base" module Css # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb index 2c20834611..ba20bcd32a 100644 --- a/railties/lib/rails/generators/erb.rb +++ b/railties/lib/rails/generators/erb.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "named_base" +require "rails/generators/named_base" module Erb # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/erb/controller/controller_generator.rb b/railties/lib/rails/generators/erb/controller/controller_generator.rb index 1a6c84288b..8e13744b2a 100644 --- a/railties/lib/rails/generators/erb/controller/controller_generator.rb +++ b/railties/lib/rails/generators/erb/controller/controller_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../erb" +require "rails/generators/erb" module Erb # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb index 5774d86c8e..e2ea66415f 100644 --- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../erb" +require "rails/generators/erb" module Erb # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb index e80c6d4b7d..2fc04e4094 100644 --- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../erb" -require_relative "../../resource_helpers" +require "rails/generators/erb" +require "rails/generators/resource_helpers" module Erb # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/js/assets/assets_generator.rb b/railties/lib/rails/generators/js/assets/assets_generator.rb index ea151aa04e..9d32c666dc 100644 --- a/railties/lib/rails/generators/js/assets/assets_generator.rb +++ b/railties/lib/rails/generators/js/assets/assets_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../named_base" +require "rails/generators/named_base" module Js # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb index 7162b8c0b4..1cbccfe461 100644 --- a/railties/lib/rails/generators/migration.rb +++ b/railties/lib/rails/generators/migration.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/concern" -require_relative "actions/create_migration" +require "rails/generators/actions/create_migration" module Rails module Generators diff --git a/railties/lib/rails/generators/model_helpers.rb b/railties/lib/rails/generators/model_helpers.rb index aa3564476a..50078404b3 100644 --- a/railties/lib/rails/generators/model_helpers.rb +++ b/railties/lib/rails/generators/model_helpers.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "active_model" +require "rails/generators/active_model" module Rails module Generators diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 5f602f1d52..44f5ab45d3 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require "active_support/core_ext/module/introspection" -require_relative "base" -require_relative "generated_attribute" +require "rails/generators/base" +require "rails/generators/generated_attribute" module Rails module Generators diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 23fdf03b05..39ef0d62de 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../app_base" +require "rails/generators/app_base" module Rails module ActionMethods # :nodoc: @@ -161,7 +161,7 @@ module Rails def master_key return if options[:pretend] - require_relative "../master_key/master_key_generator" + require "rails/generators/rails/master_key/master_key_generator" after_bundle do Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet]).add_master_key_file @@ -171,7 +171,7 @@ module Rails def credentials return if options[:pretend] - require_relative "../credentials/credentials_generator" + require "rails/generators/rails/credentials/credentials_generator" after_bundle do Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently @@ -559,7 +559,7 @@ module Rails def handle_version_request!(argument) if ["--version", "-v"].include?(argument) - require_relative "../../../version" + require "rails/version" puts "Rails #{Rails::VERSION::STRING}" exit(0) end diff --git a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb index 21ca566818..52cb4bd8bf 100644 --- a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb +++ b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../base" -require_relative "../master_key/master_key_generator" +require "rails/generators/base" +require "rails/generators/rails/master_key/master_key_generator" require "active_support/encrypted_configuration" module Rails diff --git a/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb b/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb index d054e8cad2..1aa7a2622a 100644 --- a/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb +++ b/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../base" -require_relative "../../../secrets" +require "rails/generators/base" +require "rails/secrets" module Rails module Generators diff --git a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb index 395687974a..23dbed20ac 100644 --- a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb +++ b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../base" +require "rails/generators/base" require "pathname" require "active_support/encrypted_file" diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb index 1dca03e0bb..de4de2cae2 100644 --- a/railties/lib/rails/generators/rails/model/model_generator.rb +++ b/railties/lib/rails/generators/rails/model/model_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../model_helpers" +require "rails/generators/model_helpers" module Rails module Generators diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index eb941adf95..d0dfb04162 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/hash/slice" -require_relative "../app/app_generator" +require "rails/generators/rails/app/app_generator" require "date" module Rails diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb index 74b1574863..3ba25ef0fe 100644 --- a/railties/lib/rails/generators/rails/resource/resource_generator.rb +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../resource_helpers" -require_relative "../model/model_generator" +require "rails/generators/resource_helpers" +require "rails/generators/rails/model/model_generator" module Rails module Generators diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb index c0fb873d36..8beb7416c0 100644 --- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../resource/resource_generator" +require "rails/generators/rails/resource/resource_generator" module Rails module Generators 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 601a4b3a6e..7030561a33 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 @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../resource_helpers" +require "rails/generators/resource_helpers" module Rails module Generators diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb index 8bd5110940..a146a8fda6 100644 --- a/railties/lib/rails/generators/resource_helpers.rb +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "active_model" -require_relative "model_helpers" +require "rails/generators/active_model" +require "rails/generators/model_helpers" module Rails module Generators diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb index 9c06eed95a..5c71bf0be9 100644 --- a/railties/lib/rails/generators/test_case.rb +++ b/railties/lib/rails/generators/test_case.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require_relative "../generators" -require_relative "testing/behaviour" -require_relative "testing/setup_and_teardown" -require_relative "testing/assertions" +require "rails/generators" +require "rails/generators/testing/behaviour" +require "rails/generators/testing/setup_and_teardown" +require "rails/generators/testing/assertions" require "fileutils" module Rails diff --git a/railties/lib/rails/generators/test_unit.rb b/railties/lib/rails/generators/test_unit.rb index 1aa03ace99..1005ac557c 100644 --- a/railties/lib/rails/generators/test_unit.rb +++ b/railties/lib/rails/generators/test_unit.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "named_base" +require "rails/generators/named_base" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb index 3947b74205..1a9ac6bf2a 100644 --- a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb +++ b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: 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 9f79e9eee1..19be4f2f51 100644 --- a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb +++ b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb index 1c60c3573f..77308dcf7d 100644 --- a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb +++ b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb index c859ba5e4e..ae307c5cd9 100644 --- a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb +++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/job/job_generator.rb b/railties/lib/rails/generators/test_unit/job/job_generator.rb index cbbcd1cd4e..9225af4e0c 100644 --- a/railties/lib/rails/generators/test_unit/job/job_generator.rb +++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: 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 45f82158e9..610d47a729 100644 --- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb index 2b1cc50c20..02d7502592 100644 --- a/railties/lib/rails/generators/test_unit/model/model_generator.rb +++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb index 10fe27b2c3..0657bc2389 100644 --- a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb index 23d5c63614..b6c13b41ae 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../test_unit" -require_relative "../../resource_helpers" +require "rails/generators/test_unit" +require "rails/generators/resource_helpers" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/test_unit/system/system_generator.rb b/railties/lib/rails/generators/test_unit/system/system_generator.rb index d3acbfd738..08504d4124 100644 --- a/railties/lib/rails/generators/test_unit/system/system_generator.rb +++ b/railties/lib/rails/generators/test_unit/system/system_generator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../test_unit" +require "rails/generators/test_unit" module TestUnit # :nodoc: module Generators # :nodoc: diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb index 97a09c1166..6ab88bd59f 100644 --- a/railties/lib/rails/generators/testing/behaviour.rb +++ b/railties/lib/rails/generators/testing/behaviour.rb @@ -6,7 +6,7 @@ require "active_support/core_ext/hash/reverse_merge" require "active_support/core_ext/kernel/reporting" require "active_support/testing/stream" require "active_support/concern" -require_relative "../../generators" +require "rails/generators" module Rails module Generators diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb index 6535b5b6ad..b4f4a5922a 100644 --- a/railties/lib/rails/info_controller.rb +++ b/railties/lib/rails/info_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "application_controller" +require "rails/application_controller" require "action_dispatch/routing/inspector" class Rails::InfoController < Rails::ApplicationController # :nodoc: diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb index 6bd3161fdd..66636e5d6b 100644 --- a/railties/lib/rails/mailers_controller.rb +++ b/railties/lib/rails/mailers_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "application_controller" +require "rails/application_controller" class Rails::MailersController < Rails::ApplicationController # :nodoc: prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH diff --git a/railties/lib/rails/plugin/test.rb b/railties/lib/rails/plugin/test.rb index 2426521e35..18b6fd1757 100644 --- a/railties/lib/rails/plugin/test.rb +++ b/railties/lib/rails/plugin/test.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../test_unit/runner" -require_relative "../test_unit/reporter" +require "rails/test_unit/runner" +require "rails/test_unit/reporter" Rails::TestUnitReporter.executable = "bin/test" diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index 42c4a82b1b..88dd932370 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "initializable" +require "rails/initializable" require "active_support/inflector" require "active_support/core_ext/module/introspection" require "active_support/core_ext/module/delegation" diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb index d9997759f2..48853129bc 100644 --- a/railties/lib/rails/railtie/configuration.rb +++ b/railties/lib/rails/railtie/configuration.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../configuration" +require "rails/configuration" module Rails class Railtie diff --git a/railties/lib/rails/tasks/annotations.rake b/railties/lib/rails/tasks/annotations.rake index 931f81ac7e..93bc66e2db 100644 --- a/railties/lib/rails/tasks/annotations.rake +++ b/railties/lib/rails/tasks/annotations.rake @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../source_annotation_extractor" +require "rails/source_annotation_extractor" desc "Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)" task :notes do diff --git a/railties/lib/rails/tasks/dev.rake b/railties/lib/rails/tasks/dev.rake index 90fd3ccd8b..5aea6f7dc5 100644 --- a/railties/lib/rails/tasks/dev.rake +++ b/railties/lib/rails/tasks/dev.rake @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../dev_caching" +require "rails/dev_caching" namespace :dev do desc "Toggle development mode caching on/off" diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake index 3ce49a1641..c24d1ad532 100644 --- a/railties/lib/rails/tasks/engine.rake +++ b/railties/lib/rails/tasks/engine.rake @@ -8,7 +8,7 @@ task "load_app" do task update: [ "update:bin" ] namespace :update do - require_relative "../engine/updater" + require "rails/engine/updater" # desc "Adds new executables to the engine bin/ directory" task :bin do Rails::Engine::Updater.run(:create_bin_files) diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 8931aabcda..7dfcd14bd0 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -9,8 +9,8 @@ namespace :app do template = ENV["LOCATION"] raise "No LOCATION value given. Please set LOCATION either as path to a file or a URL" if template.blank? template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} - require_relative "../generators" - require_relative "../generators/rails/app/app_generator" + require "rails/generators" + require "rails/generators/rails/app/app_generator" generator = Rails::Generators::AppGenerator.new [Rails.root], {}, { destination_root: Rails.root } generator.apply template, verbose: false end @@ -38,7 +38,7 @@ namespace :app do end namespace :update do - require_relative "../app_updater" + require "rails/app_updater" # desc "Update config/boot.rb from your current rails install" task :configs do diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake index 0449a13586..594db91eec 100644 --- a/railties/lib/rails/tasks/statistics.rake +++ b/railties/lib/rails/tasks/statistics.rake @@ -26,6 +26,6 @@ end.select { |name, dir| File.directory?(dir) } desc "Report code statistics (KLOCs, etc) from the application or engine" task :stats do - require_relative "../code_statistics" + require "rails/code_statistics" CodeStatistics.new(*STATS_DIRECTORIES).to_s end diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index f755474488..732c5c1e1f 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -8,7 +8,7 @@ require "active_support/test_case" require "action_controller" require "action_controller/test_case" require "action_dispatch/testing/integration" -require_relative "generators/test_case" +require "rails/generators/test_case" require "active_support/testing/autorun" diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb index 05f9a177d0..42b6daa3d1 100644 --- a/railties/lib/rails/test_unit/railtie.rb +++ b/railties/lib/rails/test_unit/railtie.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "line_filtering" +require "rails/test_unit/line_filtering" if defined?(Rake.application) && Rake.application.top_level_tasks.grep(/^(default$|test(:|$))/).any? ENV["RAILS_ENV"] ||= Rake.application.options.show_tasks ? "development" : "test" diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake index 18d699f806..32ac27a135 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -2,7 +2,7 @@ gem "minitest" require "minitest" -require_relative "runner" +require "rails/test_unit/runner" task default: :test diff --git a/railties/lib/rails/welcome_controller.rb b/railties/lib/rails/welcome_controller.rb index 0cb43e0f31..5b84b57679 100644 --- a/railties/lib/rails/welcome_controller.rb +++ b/railties/lib/rails/welcome_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "application_controller" +require "rails/application_controller" class Rails::WelcomeController < Rails::ApplicationController # :nodoc: layout false -- cgit v1.2.3 From 589dd0f6c9b5d6fe9de88b7bdabe83681abde2a4 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:11:29 +0900 Subject: [Active Support] require_relative => require This basically reverts 8da30ad6be34339124ba4cb4e36aea260dda12bc --- activesupport/lib/active_support.rb | 10 ++++----- activesupport/lib/active_support/all.rb | 4 ++-- activesupport/lib/active_support/benchmarkable.rb | 4 ++-- activesupport/lib/active_support/cache.rb | 14 ++++++------ .../lib/active_support/cache/file_store.rb | 6 ++--- .../lib/active_support/cache/mem_cache_store.rb | 4 ++-- .../active_support/cache/strategy/local_cache.rb | 6 ++--- activesupport/lib/active_support/callbacks.rb | 16 ++++++------- activesupport/lib/active_support/configurable.rb | 8 +++---- activesupport/lib/active_support/core_ext/array.rb | 14 ++++++------ .../active_support/core_ext/array/conversions.rb | 12 +++++----- .../lib/active_support/core_ext/array/inquiry.rb | 2 +- .../lib/active_support/core_ext/big_decimal.rb | 2 +- activesupport/lib/active_support/core_ext/class.rb | 4 ++-- .../lib/active_support/core_ext/class/attribute.rb | 6 ++--- .../core_ext/class/attribute_accessors.rb | 2 +- activesupport/lib/active_support/core_ext/date.rb | 10 ++++----- .../lib/active_support/core_ext/date/acts_like.rb | 2 +- .../active_support/core_ext/date/calculations.rb | 10 ++++----- .../active_support/core_ext/date/conversions.rb | 6 ++--- .../lib/active_support/core_ext/date/zones.rb | 2 +- .../core_ext/date_and_time/calculations.rb | 2 +- .../core_ext/date_and_time/compatibility.rb | 2 +- .../lib/active_support/core_ext/date_time.rb | 10 ++++----- .../active_support/core_ext/date_time/acts_like.rb | 2 +- .../core_ext/date_time/compatibility.rb | 4 ++-- .../core_ext/date_time/conversions.rb | 8 +++---- activesupport/lib/active_support/core_ext/file.rb | 2 +- activesupport/lib/active_support/core_ext/hash.rb | 18 +++++++-------- .../active_support/core_ext/hash/conversions.rb | 18 +++++++-------- .../core_ext/hash/indifferent_access.rb | 2 +- .../lib/active_support/core_ext/integer.rb | 6 ++--- .../active_support/core_ext/integer/inflections.rb | 2 +- .../lib/active_support/core_ext/integer/time.rb | 4 ++-- .../lib/active_support/core_ext/kernel.rb | 8 +++---- .../lib/active_support/core_ext/kernel/concern.rb | 2 +- .../lib/active_support/core_ext/module.rb | 24 ++++++++++---------- .../core_ext/module/attribute_accessors.rb | 4 ++-- .../module/attribute_accessors_per_thread.rb | 4 ++-- .../active_support/core_ext/module/concerning.rb | 2 +- .../active_support/core_ext/module/delegation.rb | 2 +- .../core_ext/module/introspection.rb | 2 +- .../active_support/core_ext/module/reachable.rb | 4 ++-- .../core_ext/module/remove_method.rb | 2 +- .../lib/active_support/core_ext/numeric.rb | 8 +++---- .../active_support/core_ext/numeric/conversions.rb | 6 ++--- .../lib/active_support/core_ext/numeric/time.rb | 10 ++++----- .../lib/active_support/core_ext/object.rb | 24 ++++++++++---------- .../lib/active_support/core_ext/object/blank.rb | 2 +- .../active_support/core_ext/object/conversions.rb | 8 +++---- .../lib/active_support/core_ext/object/deep_dup.rb | 2 +- .../lib/active_support/core_ext/object/json.rb | 14 ++++++------ .../lib/active_support/core_ext/object/to_param.rb | 2 +- .../active_support/core_ext/object/with_options.rb | 2 +- activesupport/lib/active_support/core_ext/range.rb | 8 +++---- .../lib/active_support/core_ext/string.rb | 26 +++++++++++----------- .../active_support/core_ext/string/conversions.rb | 2 +- .../active_support/core_ext/string/inflections.rb | 4 ++-- .../lib/active_support/core_ext/string/inquiry.rb | 2 +- .../active_support/core_ext/string/multibyte.rb | 2 +- .../core_ext/string/output_safety.rb | 6 ++--- .../lib/active_support/core_ext/string/zones.rb | 4 ++-- activesupport/lib/active_support/core_ext/time.rb | 10 ++++----- .../lib/active_support/core_ext/time/acts_like.rb | 2 +- .../active_support/core_ext/time/calculations.rb | 12 +++++----- .../active_support/core_ext/time/compatibility.rb | 4 ++-- .../active_support/core_ext/time/conversions.rb | 4 ++-- .../lib/active_support/core_ext/time/zones.rb | 6 ++--- activesupport/lib/active_support/dependencies.rb | 22 +++++++++--------- .../lib/active_support/dependencies/autoload.rb | 2 +- .../lib/active_support/dependencies/interlock.rb | 2 +- activesupport/lib/active_support/deprecation.rb | 14 ++++++------ .../lib/active_support/deprecation/behaviors.rb | 4 ++-- .../deprecation/constant_accessor.rb | 2 +- .../deprecation/instance_delegator.rb | 4 ++-- .../active_support/deprecation/method_wrappers.rb | 4 ++-- .../active_support/deprecation/proxy_wrappers.rb | 4 ++-- activesupport/lib/active_support/duration.rb | 10 ++++----- .../lib/active_support/duration/iso8601_parser.rb | 2 +- .../active_support/duration/iso8601_serializer.rb | 4 ++-- .../lib/active_support/execution_wrapper.rb | 2 +- activesupport/lib/active_support/executor.rb | 2 +- .../lib/active_support/file_update_checker.rb | 2 +- .../active_support/hash_with_indifferent_access.rb | 4 ++-- activesupport/lib/active_support/i18n.rb | 8 +++---- activesupport/lib/active_support/i18n_railtie.rb | 4 ++-- activesupport/lib/active_support/inflections.rb | 2 +- activesupport/lib/active_support/inflector.rb | 10 ++++----- .../lib/active_support/inflector/inflections.rb | 6 ++--- .../lib/active_support/inflector/methods.rb | 4 ++-- .../lib/active_support/inflector/transliterate.rb | 4 ++-- activesupport/lib/active_support/json.rb | 4 ++-- activesupport/lib/active_support/json/decoding.rb | 4 ++-- activesupport/lib/active_support/json/encoding.rb | 4 ++-- activesupport/lib/active_support/log_subscriber.rb | 6 ++--- .../active_support/log_subscriber/test_helper.rb | 6 ++--- activesupport/lib/active_support/logger.rb | 4 ++-- activesupport/lib/active_support/logger_silence.rb | 4 ++-- .../lib/active_support/logger_thread_safe_level.rb | 2 +- .../lib/active_support/message_encryptor.rb | 6 ++--- .../lib/active_support/message_verifier.rb | 8 +++---- .../lib/active_support/multibyte/chars.rb | 10 ++++----- activesupport/lib/active_support/notifications.rb | 6 ++--- .../number_helper/number_converter.rb | 10 ++++----- .../number_helper/number_to_currency_converter.rb | 2 +- activesupport/lib/active_support/option_merger.rb | 2 +- .../lib/active_support/ordered_options.rb | 2 +- .../lib/active_support/per_thread_registry.rb | 2 +- activesupport/lib/active_support/rails.rb | 14 ++++++------ activesupport/lib/active_support/railtie.rb | 6 ++--- activesupport/lib/active_support/reloader.rb | 2 +- activesupport/lib/active_support/rescuable.rb | 6 ++--- activesupport/lib/active_support/subscriber.rb | 4 ++-- activesupport/lib/active_support/tagged_logging.rb | 6 ++--- activesupport/lib/active_support/test_case.rb | 20 ++++++++--------- .../lib/active_support/testing/constant_lookup.rb | 4 ++-- .../lib/active_support/testing/deprecation.rb | 4 ++-- .../active_support/testing/setup_and_teardown.rb | 4 ++-- .../lib/active_support/testing/time_helpers.rb | 4 ++-- activesupport/lib/active_support/time.rb | 14 ++++++------ activesupport/lib/active_support/time_with_zone.rb | 8 +++---- .../lib/active_support/values/time_zone.rb | 2 +- activesupport/lib/active_support/xml_mini.rb | 8 +++---- activesupport/lib/active_support/xml_mini/jdom.rb | 2 +- .../lib/active_support/xml_mini/libxml.rb | 2 +- .../lib/active_support/xml_mini/libxmlsax.rb | 2 +- .../lib/active_support/xml_mini/nokogiri.rb | 2 +- .../lib/active_support/xml_mini/nokogirisax.rb | 2 +- activesupport/lib/active_support/xml_mini/rexml.rb | 4 ++-- 129 files changed, 386 insertions(+), 386 deletions(-) diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 79b12f36b9..c75b5d630c 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -24,11 +24,11 @@ #++ require "securerandom" -require_relative "active_support/dependencies/autoload" -require_relative "active_support/version" -require_relative "active_support/logger" -require_relative "active_support/lazy_load_hooks" -require_relative "active_support/core_ext/date_and_time/compatibility" +require "active_support/dependencies/autoload" +require "active_support/version" +require "active_support/logger" +require "active_support/lazy_load_hooks" +require "active_support/core_ext/date_and_time/compatibility" module ActiveSupport extend ActiveSupport::Autoload diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb index 6638b25419..4adf446af8 100644 --- a/activesupport/lib/active_support/all.rb +++ b/activesupport/lib/active_support/all.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true require "active_support" -require_relative "time" -require_relative "core_ext" +require "active_support/time" +require "active_support/core_ext" diff --git a/activesupport/lib/active_support/benchmarkable.rb b/activesupport/lib/active_support/benchmarkable.rb index 5573f6750e..f481d68198 100644 --- a/activesupport/lib/active_support/benchmarkable.rb +++ b/activesupport/lib/active_support/benchmarkable.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "core_ext/benchmark" -require_relative "core_ext/hash/keys" +require "active_support/core_ext/benchmark" +require "active_support/core_ext/hash/keys" module ActiveSupport module Benchmarkable diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 49d8965cb1..976104e0c4 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true require "zlib" -require_relative "core_ext/array/extract_options" -require_relative "core_ext/array/wrap" -require_relative "core_ext/module/attribute_accessors" -require_relative "core_ext/numeric/bytes" -require_relative "core_ext/numeric/time" -require_relative "core_ext/object/to_param" -require_relative "core_ext/string/inflections" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/array/wrap" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/numeric/bytes" +require "active_support/core_ext/numeric/time" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/string/inflections" module ActiveSupport # See ActiveSupport::Cache::Store for documentation. diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index f98d343ccd..0812cc34c7 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../core_ext/marshal" -require_relative "../core_ext/file/atomic" -require_relative "../core_ext/string/conversions" +require "active_support/core_ext/marshal" +require "active_support/core_ext/file/atomic" +require "active_support/core_ext/string/conversions" require "uri/common" module ActiveSupport diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index d7bd914722..9242a334f2 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -8,8 +8,8 @@ rescue LoadError => e end require "digest/md5" -require_relative "../core_ext/marshal" -require_relative "../core_ext/array/extract_options" +require "active_support/core_ext/marshal" +require "active_support/core_ext/array/extract_options" module ActiveSupport module Cache diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index a3eef1190a..aaa9638fa8 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../../core_ext/object/duplicable" -require_relative "../../core_ext/string/inflections" -require_relative "../../per_thread_registry" +require "active_support/core_ext/object/duplicable" +require "active_support/core_ext/string/inflections" +require "active_support/per_thread_registry" module ActiveSupport module Cache diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index e1cbfc945c..eb6a4c3ce7 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -require_relative "concern" -require_relative "descendants_tracker" -require_relative "core_ext/array/extract_options" -require_relative "core_ext/class/attribute" -require_relative "core_ext/kernel/reporting" -require_relative "core_ext/kernel/singleton_class" -require_relative "core_ext/string/filters" -require_relative "deprecation" +require "active_support/concern" +require "active_support/descendants_tracker" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/class/attribute" +require "active_support/core_ext/kernel/reporting" +require "active_support/core_ext/kernel/singleton_class" +require "active_support/core_ext/string/filters" +require "active_support/deprecation" require "thread" module ActiveSupport diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb index 8e8e27b4ec..4d6f7819bb 100644 --- a/activesupport/lib/active_support/configurable.rb +++ b/activesupport/lib/active_support/configurable.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require_relative "concern" -require_relative "ordered_options" -require_relative "core_ext/array/extract_options" -require_relative "core_ext/regexp" +require "active_support/concern" +require "active_support/ordered_options" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/regexp" module ActiveSupport # Configurable provides a config method to store and retrieve diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb index fdc209ef68..6d83b76882 100644 --- a/activesupport/lib/active_support/core_ext/array.rb +++ b/activesupport/lib/active_support/core_ext/array.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require_relative "array/wrap" -require_relative "array/access" -require_relative "array/conversions" -require_relative "array/extract_options" -require_relative "array/grouping" -require_relative "array/prepend_and_append" -require_relative "array/inquiry" +require "active_support/core_ext/array/wrap" +require "active_support/core_ext/array/access" +require "active_support/core_ext/array/conversions" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/array/grouping" +require "active_support/core_ext/array/prepend_and_append" +require "active_support/core_ext/array/inquiry" diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 6e17a81f0f..ea688ed2ea 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require_relative "../../xml_mini" -require_relative "../hash/keys" -require_relative "../string/inflections" -require_relative "../object/to_param" -require_relative "../object/to_query" +require "active_support/xml_mini" +require "active_support/core_ext/hash/keys" +require "active_support/core_ext/string/inflections" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/to_query" class Array # Converts the array to a comma-separated sentence where the last element is @@ -181,7 +181,7 @@ class Array # # def to_xml(options = {}) - require_relative "../../builder" unless defined?(Builder) + require "active_support/builder" unless defined?(Builder) options = options.dup options[:indent] ||= 2 diff --git a/activesupport/lib/active_support/core_ext/array/inquiry.rb b/activesupport/lib/active_support/core_ext/array/inquiry.rb index 4f4a9fa361..92c61bf201 100644 --- a/activesupport/lib/active_support/core_ext/array/inquiry.rb +++ b/activesupport/lib/active_support/core_ext/array/inquiry.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../array_inquirer" +require "active_support/array_inquirer" class Array # Wraps the array in an +ArrayInquirer+ object, which gives a friendlier way diff --git a/activesupport/lib/active_support/core_ext/big_decimal.rb b/activesupport/lib/active_support/core_ext/big_decimal.rb index 5568db689b..9e6a9d6331 100644 --- a/activesupport/lib/active_support/core_ext/big_decimal.rb +++ b/activesupport/lib/active_support/core_ext/big_decimal.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -require_relative "big_decimal/conversions" +require "active_support/core_ext/big_decimal/conversions" diff --git a/activesupport/lib/active_support/core_ext/class.rb b/activesupport/lib/active_support/core_ext/class.rb index 65e1d68399..1c110fd07b 100644 --- a/activesupport/lib/active_support/core_ext/class.rb +++ b/activesupport/lib/active_support/core_ext/class.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true -require_relative "class/attribute" -require_relative "class/subclasses" +require "active_support/core_ext/class/attribute" +require "active_support/core_ext/class/subclasses" diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb index 3453fc4ac6..7928efb871 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../kernel/singleton_class" -require_relative "../module/redefine_method" -require_relative "../array/extract_options" +require "active_support/core_ext/kernel/singleton_class" +require "active_support/core_ext/module/redefine_method" +require "active_support/core_ext/array/extract_options" class Class # Declare a class-level attribute whose value is inheritable by subclasses. diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb index b4dbcc6698..a77354e153 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb @@ -3,4 +3,4 @@ # cattr_* became mattr_* aliases in 7dfbd91b0780fbd6a1dd9bfbc176e10894871d2d, # but we keep this around for libraries that directly require it knowing they # want cattr_*. No need to deprecate. -require_relative "../module/attribute_accessors" +require "active_support/core_ext/module/attribute_accessors" diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb index 2a2ed5496f..cce73f2db2 100644 --- a/activesupport/lib/active_support/core_ext/date.rb +++ b/activesupport/lib/active_support/core_ext/date.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "date/acts_like" -require_relative "date/blank" -require_relative "date/calculations" -require_relative "date/conversions" -require_relative "date/zones" +require "active_support/core_ext/date/acts_like" +require "active_support/core_ext/date/blank" +require "active_support/core_ext/date/calculations" +require "active_support/core_ext/date/conversions" +require "active_support/core_ext/date/zones" diff --git a/activesupport/lib/active_support/core_ext/date/acts_like.rb b/activesupport/lib/active_support/core_ext/date/acts_like.rb index 81769129b5..c8077f3774 100644 --- a/activesupport/lib/active_support/core_ext/date/acts_like.rb +++ b/activesupport/lib/active_support/core_ext/date/acts_like.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../object/acts_like" +require "active_support/core_ext/object/acts_like" class Date # Duck-types as a Date-like class. See Object#acts_like?. diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 1a19ee8b1e..1cd7acb05d 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true require "date" -require_relative "../../duration" -require_relative "../object/acts_like" -require_relative "zones" -require_relative "../time/zones" -require_relative "../date_and_time/calculations" +require "active_support/duration" +require "active_support/core_ext/object/acts_like" +require "active_support/core_ext/date/zones" +require "active_support/core_ext/time/zones" +require "active_support/core_ext/date_and_time/calculations" class Date include DateAndTime::Calculations diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 88fb572725..870119dc7f 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true require "date" -require_relative "../../inflector/methods" -require_relative "zones" -require_relative "../module/redefine_method" +require "active_support/inflector/methods" +require "active_support/core_ext/date/zones" +require "active_support/core_ext/module/redefine_method" class Date DATE_FORMATS = { diff --git a/activesupport/lib/active_support/core_ext/date/zones.rb b/activesupport/lib/active_support/core_ext/date/zones.rb index 1aed070df0..2dcf97cff8 100644 --- a/activesupport/lib/active_support/core_ext/date/zones.rb +++ b/activesupport/lib/active_support/core_ext/date/zones.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "date" -require_relative "../date_and_time/zones" +require "active_support/core_ext/date_and_time/zones" class Date include DateAndTime::Zones diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index 8546f7e57b..265c6949e1 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../object/try" +require "active_support/core_ext/object/try" module DateAndTime module Calculations diff --git a/activesupport/lib/active_support/core_ext/date_and_time/compatibility.rb b/activesupport/lib/active_support/core_ext/date_and_time/compatibility.rb index 5b3aabf6a5..d33c36ef73 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/compatibility.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/compatibility.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../module/attribute_accessors" +require "active_support/core_ext/module/attribute_accessors" module DateAndTime module Compatibility diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb index b3d6773e4f..790dbeec1b 100644 --- a/activesupport/lib/active_support/core_ext/date_time.rb +++ b/activesupport/lib/active_support/core_ext/date_time.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "date_time/acts_like" -require_relative "date_time/blank" -require_relative "date_time/calculations" -require_relative "date_time/compatibility" -require_relative "date_time/conversions" +require "active_support/core_ext/date_time/acts_like" +require "active_support/core_ext/date_time/blank" +require "active_support/core_ext/date_time/calculations" +require "active_support/core_ext/date_time/compatibility" +require "active_support/core_ext/date_time/conversions" diff --git a/activesupport/lib/active_support/core_ext/date_time/acts_like.rb b/activesupport/lib/active_support/core_ext/date_time/acts_like.rb index 02acb5fe21..5dccdfe219 100644 --- a/activesupport/lib/active_support/core_ext/date_time/acts_like.rb +++ b/activesupport/lib/active_support/core_ext/date_time/acts_like.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "date" -require_relative "../object/acts_like" +require "active_support/core_ext/object/acts_like" class DateTime # Duck-types as a Date-like class. See Object#acts_like?. diff --git a/activesupport/lib/active_support/core_ext/date_time/compatibility.rb b/activesupport/lib/active_support/core_ext/date_time/compatibility.rb index bb9714545e..424f64d6fa 100644 --- a/activesupport/lib/active_support/core_ext/date_time/compatibility.rb +++ b/activesupport/lib/active_support/core_ext/date_time/compatibility.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../date_and_time/compatibility" -require_relative "../module/redefine_method" +require "active_support/core_ext/date_and_time/compatibility" +require "active_support/core_ext/module/redefine_method" class DateTime include DateAndTime::Compatibility diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb index e4c8f9898d..29725c89f7 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true require "date" -require_relative "../../inflector/methods" -require_relative "../time/conversions" -require_relative "calculations" -require_relative "../../values/time_zone" +require "active_support/inflector/methods" +require "active_support/core_ext/time/conversions" +require "active_support/core_ext/date_time/calculations" +require "active_support/values/time_zone" class DateTime # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats. diff --git a/activesupport/lib/active_support/core_ext/file.rb b/activesupport/lib/active_support/core_ext/file.rb index 3c2364167d..64553bfa4e 100644 --- a/activesupport/lib/active_support/core_ext/file.rb +++ b/activesupport/lib/active_support/core_ext/file.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -require_relative "file/atomic" +require "active_support/core_ext/file/atomic" diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb index a74a8c15a6..e19aeaa983 100644 --- a/activesupport/lib/active_support/core_ext/hash.rb +++ b/activesupport/lib/active_support/core_ext/hash.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require_relative "hash/compact" -require_relative "hash/conversions" -require_relative "hash/deep_merge" -require_relative "hash/except" -require_relative "hash/indifferent_access" -require_relative "hash/keys" -require_relative "hash/reverse_merge" -require_relative "hash/slice" -require_relative "hash/transform_values" +require "active_support/core_ext/hash/compact" +require "active_support/core_ext/hash/conversions" +require "active_support/core_ext/hash/deep_merge" +require "active_support/core_ext/hash/except" +require "active_support/core_ext/hash/indifferent_access" +require "active_support/core_ext/hash/keys" +require "active_support/core_ext/hash/reverse_merge" +require "active_support/core_ext/hash/slice" +require "active_support/core_ext/hash/transform_values" diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 0c0701134e..11d28d12a1 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -require_relative "../../xml_mini" -require_relative "../../time" -require_relative "../object/blank" -require_relative "../object/to_param" -require_relative "../object/to_query" -require_relative "../array/wrap" -require_relative "reverse_merge" -require_relative "../string/inflections" +require "active_support/xml_mini" +require "active_support/time" +require "active_support/core_ext/object/blank" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/to_query" +require "active_support/core_ext/array/wrap" +require "active_support/core_ext/hash/reverse_merge" +require "active_support/core_ext/string/inflections" class Hash # Returns a string containing an XML representation of its receiver: @@ -73,7 +73,7 @@ class Hash # configure your own builder with the :builder option. The method also accepts # options like :dasherize and friends, they are forwarded to the builder. def to_xml(options = {}) - require_relative "../../builder" unless defined?(Builder) + require "active_support/builder" unless defined?(Builder) options = options.dup options[:indent] ||= 2 diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb index 4681d986db..a38f33f128 100644 --- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb +++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../hash_with_indifferent_access" +require "active_support/hash_with_indifferent_access" class Hash # Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver: diff --git a/activesupport/lib/active_support/core_ext/integer.rb b/activesupport/lib/active_support/core_ext/integer.rb index df80e7ffdb..d22701306a 100644 --- a/activesupport/lib/active_support/core_ext/integer.rb +++ b/activesupport/lib/active_support/core_ext/integer.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -require_relative "integer/multiple" -require_relative "integer/inflections" -require_relative "integer/time" +require "active_support/core_ext/integer/multiple" +require "active_support/core_ext/integer/inflections" +require "active_support/core_ext/integer/time" diff --git a/activesupport/lib/active_support/core_ext/integer/inflections.rb b/activesupport/lib/active_support/core_ext/integer/inflections.rb index 7192e92346..aef3266f28 100644 --- a/activesupport/lib/active_support/core_ext/integer/inflections.rb +++ b/activesupport/lib/active_support/core_ext/integer/inflections.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../inflector" +require "active_support/inflector" class Integer # Ordinalize turns a number into an ordinal string used to denote the diff --git a/activesupport/lib/active_support/core_ext/integer/time.rb b/activesupport/lib/active_support/core_ext/integer/time.rb index 30c8f3bcf2..74c73ab064 100644 --- a/activesupport/lib/active_support/core_ext/integer/time.rb +++ b/activesupport/lib/active_support/core_ext/integer/time.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../duration" -require_relative "../numeric/time" +require "active_support/duration" +require "active_support/core_ext/numeric/time" class Integer # Enables the use of time calculations and declarations, like 45.minutes + diff --git a/activesupport/lib/active_support/core_ext/kernel.rb b/activesupport/lib/active_support/core_ext/kernel.rb index 30810ce315..0f4356fbdd 100644 --- a/activesupport/lib/active_support/core_ext/kernel.rb +++ b/activesupport/lib/active_support/core_ext/kernel.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "kernel/agnostics" -require_relative "kernel/concern" -require_relative "kernel/reporting" -require_relative "kernel/singleton_class" +require "active_support/core_ext/kernel/agnostics" +require "active_support/core_ext/kernel/concern" +require "active_support/core_ext/kernel/reporting" +require "active_support/core_ext/kernel/singleton_class" diff --git a/activesupport/lib/active_support/core_ext/kernel/concern.rb b/activesupport/lib/active_support/core_ext/kernel/concern.rb index 32af9981a5..0b2baed780 100644 --- a/activesupport/lib/active_support/core_ext/kernel/concern.rb +++ b/activesupport/lib/active_support/core_ext/kernel/concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../module/concerning" +require "active_support/core_ext/module/concerning" module Kernel module_function diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb index 8445643730..d91e3fba6a 100644 --- a/activesupport/lib/active_support/core_ext/module.rb +++ b/activesupport/lib/active_support/core_ext/module.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true -require_relative "module/aliasing" -require_relative "module/introspection" -require_relative "module/anonymous" -require_relative "module/reachable" -require_relative "module/attribute_accessors" -require_relative "module/attribute_accessors_per_thread" -require_relative "module/attr_internal" -require_relative "module/concerning" -require_relative "module/delegation" -require_relative "module/deprecation" -require_relative "module/redefine_method" -require_relative "module/remove_method" +require "active_support/core_ext/module/aliasing" +require "active_support/core_ext/module/introspection" +require "active_support/core_ext/module/anonymous" +require "active_support/core_ext/module/reachable" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/module/attribute_accessors_per_thread" +require "active_support/core_ext/module/attr_internal" +require "active_support/core_ext/module/concerning" +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/module/deprecation" +require "active_support/core_ext/module/redefine_method" +require "active_support/core_ext/module/remove_method" 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 e667c1f4a4..580baffa2b 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../array/extract_options" -require_relative "../regexp" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/regexp" # Extends the module object with class/module and instance accessors for # class/module attributes, just like the native attr* accessors for instance diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb index 90a350c5dc..4b9b6ea9bd 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../array/extract_options" -require_relative "../regexp" +require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/regexp" # Extends the module object with class/module and instance accessors for # class/module attributes, just like the native attr* accessors for instance diff --git a/activesupport/lib/active_support/core_ext/module/concerning.rb b/activesupport/lib/active_support/core_ext/module/concerning.rb index 17c202a24a..370a948eea 100644 --- a/activesupport/lib/active_support/core_ext/module/concerning.rb +++ b/activesupport/lib/active_support/core_ext/module/concerning.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../concern" +require "active_support/concern" class Module # = Bite-sized separation of concerns diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index 1840dc942f..a77f903db5 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "set" -require_relative "../regexp" +require "active_support/core_ext/regexp" class Module # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+ diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index 540385ef6f..c5bb598bd1 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../inflector" +require "active_support/inflector" class Module # Returns the name of the module containing this one. diff --git a/activesupport/lib/active_support/core_ext/module/reachable.rb b/activesupport/lib/active_support/core_ext/module/reachable.rb index 790a3cc561..e9cbda5245 100644 --- a/activesupport/lib/active_support/core_ext/module/reachable.rb +++ b/activesupport/lib/active_support/core_ext/module/reachable.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "anonymous" -require_relative "../string/inflections" +require "active_support/core_ext/module/anonymous" +require "active_support/core_ext/string/inflections" class Module def reachable? #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/module/remove_method.rb b/activesupport/lib/active_support/core_ext/module/remove_method.rb index 704f144bdd..97eb5f9eca 100644 --- a/activesupport/lib/active_support/core_ext/module/remove_method.rb +++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "redefine_method" +require "active_support/core_ext/module/redefine_method" class Module # Removes the named method, if it exists. diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb index 76e33a7cb0..0b04e359f9 100644 --- a/activesupport/lib/active_support/core_ext/numeric.rb +++ b/activesupport/lib/active_support/core_ext/numeric.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "numeric/bytes" -require_relative "numeric/time" -require_relative "numeric/inquiry" -require_relative "numeric/conversions" +require "active_support/core_ext/numeric/bytes" +require "active_support/core_ext/numeric/time" +require "active_support/core_ext/numeric/inquiry" +require "active_support/core_ext/numeric/conversions" diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb index 05528f5069..e675f32cd4 100644 --- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb +++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../big_decimal/conversions" -require_relative "../../number_helper" -require_relative "../module/deprecation" +require "active_support/core_ext/big_decimal/conversions" +require "active_support/number_helper" +require "active_support/core_ext/module/deprecation" module ActiveSupport::NumericWithFormat # Provides options for converting numbers into formatted strings. diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb index 0cee87cb86..d62b57fe27 100644 --- a/activesupport/lib/active_support/core_ext/numeric/time.rb +++ b/activesupport/lib/active_support/core_ext/numeric/time.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require_relative "../../duration" -require_relative "../time/calculations" -require_relative "../time/acts_like" -require_relative "../date/calculations" -require_relative "../date/acts_like" +require "active_support/duration" +require "active_support/core_ext/time/calculations" +require "active_support/core_ext/time/acts_like" +require "active_support/core_ext/date/calculations" +require "active_support/core_ext/date/acts_like" class Numeric # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 23f5eec8c7..efd34cc692 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -1,16 +1,16 @@ # frozen_string_literal: true -require_relative "object/acts_like" -require_relative "object/blank" -require_relative "object/duplicable" -require_relative "object/deep_dup" -require_relative "object/try" -require_relative "object/inclusion" +require "active_support/core_ext/object/acts_like" +require "active_support/core_ext/object/blank" +require "active_support/core_ext/object/duplicable" +require "active_support/core_ext/object/deep_dup" +require "active_support/core_ext/object/try" +require "active_support/core_ext/object/inclusion" -require_relative "object/conversions" -require_relative "object/instance_variables" +require "active_support/core_ext/object/conversions" +require "active_support/core_ext/object/instance_variables" -require_relative "object/json" -require_relative "object/to_param" -require_relative "object/to_query" -require_relative "object/with_options" +require "active_support/core_ext/object/json" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/to_query" +require "active_support/core_ext/object/with_options" diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb index 397adbdb5a..e42ad852dd 100644 --- a/activesupport/lib/active_support/core_ext/object/blank.rb +++ b/activesupport/lib/active_support/core_ext/object/blank.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../regexp" +require "active_support/core_ext/regexp" class Object # An object is blank if it's false, empty, or a whitespace string. diff --git a/activesupport/lib/active_support/core_ext/object/conversions.rb b/activesupport/lib/active_support/core_ext/object/conversions.rb index fdc154188a..624fb8d77c 100644 --- a/activesupport/lib/active_support/core_ext/object/conversions.rb +++ b/activesupport/lib/active_support/core_ext/object/conversions.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "to_param" -require_relative "to_query" -require_relative "../array/conversions" -require_relative "../hash/conversions" +require "active_support/core_ext/object/to_param" +require "active_support/core_ext/object/to_query" +require "active_support/core_ext/array/conversions" +require "active_support/core_ext/hash/conversions" diff --git a/activesupport/lib/active_support/core_ext/object/deep_dup.rb b/activesupport/lib/active_support/core_ext/object/deep_dup.rb index 4021b15de6..c66c5eb2d9 100644 --- a/activesupport/lib/active_support/core_ext/object/deep_dup.rb +++ b/activesupport/lib/active_support/core_ext/object/deep_dup.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "duplicable" +require "active_support/core_ext/object/duplicable" class Object # Returns a deep copy of object if it's duplicable. If it's diff --git a/activesupport/lib/active_support/core_ext/object/json.rb b/activesupport/lib/active_support/core_ext/object/json.rb index 30495313d2..0a3b875f24 100644 --- a/activesupport/lib/active_support/core_ext/object/json.rb +++ b/activesupport/lib/active_support/core_ext/object/json.rb @@ -5,14 +5,14 @@ require "json" require "bigdecimal" require "uri/generic" require "pathname" -require_relative "../big_decimal/conversions" # for #to_s -require_relative "../hash/except" -require_relative "../hash/slice" -require_relative "instance_variables" +require "active_support/core_ext/big_decimal/conversions" # for #to_s +require "active_support/core_ext/hash/except" +require "active_support/core_ext/hash/slice" +require "active_support/core_ext/object/instance_variables" require "time" -require_relative "../time/conversions" -require_relative "../date_time/conversions" -require_relative "../date/conversions" +require "active_support/core_ext/time/conversions" +require "active_support/core_ext/date_time/conversions" +require "active_support/core_ext/date/conversions" # The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting # their default behavior. That said, we need to define the basic to_json method in all of them, diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index c57488bcbc..6d2bdd70f3 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -require_relative "to_query" +require "active_support/core_ext/object/to_query" diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb index 47766f6012..b6c464db33 100644 --- a/activesupport/lib/active_support/core_ext/object/with_options.rb +++ b/activesupport/lib/active_support/core_ext/object/with_options.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../option_merger" +require "active_support/option_merger" class Object # An elegant way to factor duplication out of options passed to a series of diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb index 89bbbfcb81..51ae0ddd21 100644 --- a/activesupport/lib/active_support/core_ext/range.rb +++ b/activesupport/lib/active_support/core_ext/range.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "range/conversions" -require_relative "range/include_range" -require_relative "range/overlaps" -require_relative "range/each" +require "active_support/core_ext/range/conversions" +require "active_support/core_ext/range/include_range" +require "active_support/core_ext/range/overlaps" +require "active_support/core_ext/range/each" diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb index 491eec2fc9..757d15c51a 100644 --- a/activesupport/lib/active_support/core_ext/string.rb +++ b/activesupport/lib/active_support/core_ext/string.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require_relative "string/conversions" -require_relative "string/filters" -require_relative "string/multibyte" -require_relative "string/starts_ends_with" -require_relative "string/inflections" -require_relative "string/access" -require_relative "string/behavior" -require_relative "string/output_safety" -require_relative "string/exclude" -require_relative "string/strip" -require_relative "string/inquiry" -require_relative "string/indent" -require_relative "string/zones" +require "active_support/core_ext/string/conversions" +require "active_support/core_ext/string/filters" +require "active_support/core_ext/string/multibyte" +require "active_support/core_ext/string/starts_ends_with" +require "active_support/core_ext/string/inflections" +require "active_support/core_ext/string/access" +require "active_support/core_ext/string/behavior" +require "active_support/core_ext/string/output_safety" +require "active_support/core_ext/string/exclude" +require "active_support/core_ext/string/strip" +require "active_support/core_ext/string/inquiry" +require "active_support/core_ext/string/indent" +require "active_support/core_ext/string/zones" diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index f8f6524b2b..29a88b07ad 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "date" -require_relative "../time/calculations" +require "active_support/core_ext/time/calculations" class String # Converts a string to a Time value. diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index b5bb385033..da53739efc 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../inflector/methods" -require_relative "../../inflector/transliterate" +require "active_support/inflector/methods" +require "active_support/inflector/transliterate" # String inflections define new methods on the String class to transform names for different purposes. # For instance, you can figure out the name of a table from the name of a class. diff --git a/activesupport/lib/active_support/core_ext/string/inquiry.rb b/activesupport/lib/active_support/core_ext/string/inquiry.rb index 92069981b6..a796d5fb4f 100644 --- a/activesupport/lib/active_support/core_ext/string/inquiry.rb +++ b/activesupport/lib/active_support/core_ext/string/inquiry.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../string_inquirer" +require "active_support/string_inquirer" class String # Wraps the current string in the ActiveSupport::StringInquirer class, diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb index fba5b166a2..38224ea5da 100644 --- a/activesupport/lib/active_support/core_ext/string/multibyte.rb +++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../multibyte" +require "active_support/multibyte" class String # == Multibyte proxy diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 873213e6d7..b712200959 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true require "erb" -require_relative "../kernel/singleton_class" -require_relative "../module/redefine_method" -require_relative "../../multibyte/unicode" +require "active_support/core_ext/kernel/singleton_class" +require "active_support/core_ext/module/redefine_method" +require "active_support/multibyte/unicode" class ERB module Util diff --git a/activesupport/lib/active_support/core_ext/string/zones.rb b/activesupport/lib/active_support/core_ext/string/zones.rb index db30c03a8e..55dc231464 100644 --- a/activesupport/lib/active_support/core_ext/string/zones.rb +++ b/activesupport/lib/active_support/core_ext/string/zones.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "conversions" -require_relative "../time/zones" +require "active_support/core_ext/string/conversions" +require "active_support/core_ext/time/zones" class String # Converts String to a TimeWithZone in the current zone if Time.zone or Time.zone_default diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb index 4e16274443..c809def05f 100644 --- a/activesupport/lib/active_support/core_ext/time.rb +++ b/activesupport/lib/active_support/core_ext/time.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "time/acts_like" -require_relative "time/calculations" -require_relative "time/compatibility" -require_relative "time/conversions" -require_relative "time/zones" +require "active_support/core_ext/time/acts_like" +require "active_support/core_ext/time/calculations" +require "active_support/core_ext/time/compatibility" +require "active_support/core_ext/time/conversions" +require "active_support/core_ext/time/zones" diff --git a/activesupport/lib/active_support/core_ext/time/acts_like.rb b/activesupport/lib/active_support/core_ext/time/acts_like.rb index 309418df42..8572b49639 100644 --- a/activesupport/lib/active_support/core_ext/time/acts_like.rb +++ b/activesupport/lib/active_support/core_ext/time/acts_like.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../object/acts_like" +require "active_support/core_ext/object/acts_like" class Time # Duck-types as a Time-like class. See Object#acts_like?. diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 0e51df44ef..120768dec5 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require_relative "../../duration" -require_relative "conversions" -require_relative "../../time_with_zone" -require_relative "zones" -require_relative "../date_and_time/calculations" -require_relative "../date/calculations" +require "active_support/duration" +require "active_support/core_ext/time/conversions" +require "active_support/time_with_zone" +require "active_support/core_ext/time/zones" +require "active_support/core_ext/date_and_time/calculations" +require "active_support/core_ext/date/calculations" class Time include DateAndTime::Calculations diff --git a/activesupport/lib/active_support/core_ext/time/compatibility.rb b/activesupport/lib/active_support/core_ext/time/compatibility.rb index 147ae0f802..495e4f307b 100644 --- a/activesupport/lib/active_support/core_ext/time/compatibility.rb +++ b/activesupport/lib/active_support/core_ext/time/compatibility.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../date_and_time/compatibility" -require_relative "../module/redefine_method" +require "active_support/core_ext/date_and_time/compatibility" +require "active_support/core_ext/module/redefine_method" class Time include DateAndTime::Compatibility diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb index e3fc930ef6..345cb2832c 100644 --- a/activesupport/lib/active_support/core_ext/time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/time/conversions.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../../inflector/methods" -require_relative "../../values/time_zone" +require "active_support/inflector/methods" +require "active_support/values/time_zone" class Time DATE_FORMATS = { diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index 0aae073fb5..a5588fd488 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../../time_with_zone" -require_relative "acts_like" -require_relative "../date_and_time/zones" +require "active_support/time_with_zone" +require "active_support/core_ext/time/acts_like" +require "active_support/core_ext/date_and_time/zones" class Time include DateAndTime::Zones diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index ad2f21205f..649b900187 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -4,17 +4,17 @@ require "set" require "thread" require "concurrent/map" require "pathname" -require_relative "core_ext/module/aliasing" -require_relative "core_ext/module/attribute_accessors" -require_relative "core_ext/module/introspection" -require_relative "core_ext/module/anonymous" -require_relative "core_ext/object/blank" -require_relative "core_ext/kernel/reporting" -require_relative "core_ext/load_error" -require_relative "core_ext/name_error" -require_relative "core_ext/string/starts_ends_with" -require_relative "dependencies/interlock" -require_relative "inflector" +require "active_support/core_ext/module/aliasing" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/module/introspection" +require "active_support/core_ext/module/anonymous" +require "active_support/core_ext/object/blank" +require "active_support/core_ext/kernel/reporting" +require "active_support/core_ext/load_error" +require "active_support/core_ext/name_error" +require "active_support/core_ext/string/starts_ends_with" +require "active_support/dependencies/interlock" +require "active_support/inflector" module ActiveSupport #:nodoc: module Dependencies #:nodoc: diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb index 1c3775f5eb..1cee85d98f 100644 --- a/activesupport/lib/active_support/dependencies/autoload.rb +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../inflector/methods" +require "active_support/inflector/methods" module ActiveSupport # Autoload and eager load conveniences for your library. diff --git a/activesupport/lib/active_support/dependencies/interlock.rb b/activesupport/lib/active_support/dependencies/interlock.rb index 4e9595ed42..948be75638 100644 --- a/activesupport/lib/active_support/dependencies/interlock.rb +++ b/activesupport/lib/active_support/dependencies/interlock.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../concurrency/share_lock" +require "active_support/concurrency/share_lock" module ActiveSupport #:nodoc: module Dependencies #:nodoc: diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index e6550fdcd0..a1ad2ca465 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -14,13 +14,13 @@ module ActiveSupport # a circular require warning for active_support/deprecation.rb. # # So, we define the constant first, and load dependencies later. - require_relative "deprecation/instance_delegator" - require_relative "deprecation/behaviors" - require_relative "deprecation/reporting" - require_relative "deprecation/constant_accessor" - require_relative "deprecation/method_wrappers" - require_relative "deprecation/proxy_wrappers" - require_relative "core_ext/module/deprecation" + require "active_support/deprecation/instance_delegator" + require "active_support/deprecation/behaviors" + require "active_support/deprecation/reporting" + require "active_support/deprecation/constant_accessor" + require "active_support/deprecation/method_wrappers" + require "active_support/deprecation/proxy_wrappers" + require "active_support/core_ext/module/deprecation" include Singleton include InstanceDelegator diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index 967320bcb9..581db5f449 100644 --- a/activesupport/lib/active_support/deprecation/behaviors.rb +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../notifications" +require "active_support/notifications" module ActiveSupport # Raised when ActiveSupport::Deprecation::Behavior#behavior is set with :raise. @@ -27,7 +27,7 @@ module ActiveSupport if defined?(Rails.logger) && Rails.logger Rails.logger else - require_relative "../logger" + require "active_support/logger" ActiveSupport::Logger.new($stderr) end logger.warn message diff --git a/activesupport/lib/active_support/deprecation/constant_accessor.rb b/activesupport/lib/active_support/deprecation/constant_accessor.rb index 0dfd96d134..3d7eedf637 100644 --- a/activesupport/lib/active_support/deprecation/constant_accessor.rb +++ b/activesupport/lib/active_support/deprecation/constant_accessor.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../inflector/methods" +require "active_support/inflector/methods" module ActiveSupport class Deprecation diff --git a/activesupport/lib/active_support/deprecation/instance_delegator.rb b/activesupport/lib/active_support/deprecation/instance_delegator.rb index 539357c83e..8beda373a2 100644 --- a/activesupport/lib/active_support/deprecation/instance_delegator.rb +++ b/activesupport/lib/active_support/deprecation/instance_delegator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../core_ext/kernel/singleton_class" -require_relative "../core_ext/module/delegation" +require "active_support/core_ext/kernel/singleton_class" +require "active_support/core_ext/module/delegation" module ActiveSupport class Deprecation diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index 8c942ed9a5..d359992bf5 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../core_ext/module/aliasing" -require_relative "../core_ext/array/extract_options" +require "active_support/core_ext/module/aliasing" +require "active_support/core_ext/array/extract_options" module ActiveSupport class Deprecation diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 1920d75faf..f6c6648917 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../inflector/methods" -require_relative "../core_ext/regexp" +require "active_support/inflector/methods" +require "active_support/core_ext/regexp" module ActiveSupport class Deprecation diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 34024fa8c6..1af3411a8a 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require_relative "core_ext/array/conversions" -require_relative "core_ext/module/delegation" -require_relative "core_ext/object/acts_like" -require_relative "core_ext/string/filters" -require_relative "deprecation" +require "active_support/core_ext/array/conversions" +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/object/acts_like" +require "active_support/core_ext/string/filters" +require "active_support/deprecation" module ActiveSupport # Provides accurate date and time measurements using Date#advance and diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb index a002424cd9..9379ec7da6 100644 --- a/activesupport/lib/active_support/duration/iso8601_parser.rb +++ b/activesupport/lib/active_support/duration/iso8601_parser.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "strscan" -require_relative "../core_ext/regexp" +require "active_support/core_ext/regexp" module ActiveSupport class Duration diff --git a/activesupport/lib/active_support/duration/iso8601_serializer.rb b/activesupport/lib/active_support/duration/iso8601_serializer.rb index 985eac113f..bb177ae5b7 100644 --- a/activesupport/lib/active_support/duration/iso8601_serializer.rb +++ b/activesupport/lib/active_support/duration/iso8601_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../core_ext/object/blank" -require_relative "../core_ext/hash/transform_values" +require "active_support/core_ext/object/blank" +require "active_support/core_ext/hash/transform_values" module ActiveSupport class Duration diff --git a/activesupport/lib/active_support/execution_wrapper.rb b/activesupport/lib/active_support/execution_wrapper.rb index fd87d84795..f48c586cad 100644 --- a/activesupport/lib/active_support/execution_wrapper.rb +++ b/activesupport/lib/active_support/execution_wrapper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "callbacks" +require "active_support/callbacks" module ActiveSupport class ExecutionWrapper diff --git a/activesupport/lib/active_support/executor.rb b/activesupport/lib/active_support/executor.rb index e6487ba69d..ce391b07ec 100644 --- a/activesupport/lib/active_support/executor.rb +++ b/activesupport/lib/active_support/executor.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "execution_wrapper" +require "active_support/execution_wrapper" module ActiveSupport class Executor < ExecutionWrapper diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index bcf7b9de64..1a0bb10815 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "core_ext/time/calculations" +require "active_support/core_ext/time/calculations" module ActiveSupport # FileUpdateChecker specifies the API used by Rails to watch files diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index f8bff4fe3e..2e2ed8a25d 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "core_ext/hash/keys" -require_relative "core_ext/hash/reverse_merge" +require "active_support/core_ext/hash/keys" +require "active_support/core_ext/hash/reverse_merge" module ActiveSupport # Implements a hash where keys :foo and "foo" are considered diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 80f1475630..d60b3eff30 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require_relative "core_ext/hash/deep_merge" -require_relative "core_ext/hash/except" -require_relative "core_ext/hash/slice" +require "active_support/core_ext/hash/deep_merge" +require "active_support/core_ext/hash/except" +require "active_support/core_ext/hash/slice" begin require "i18n" rescue LoadError => e $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install" raise e end -require_relative "lazy_load_hooks" +require "active_support/lazy_load_hooks" ActiveSupport.run_load_hooks(:i18n) I18n.load_path << File.expand_path("locale/en.yml", __dir__) diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index 369aecff69..ce8bfbfd8c 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require "active_support" -require_relative "file_update_checker" -require_relative "core_ext/array/wrap" +require "active_support/file_update_checker" +require "active_support/core_ext/array/wrap" # :enddoc: diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb index e8e1657111..baf1cb3038 100644 --- a/activesupport/lib/active_support/inflections.rb +++ b/activesupport/lib/active_support/inflections.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "inflector/inflections" +require "active_support/inflector/inflections" #-- # Defines the standard inflection rules. These are the starting point for diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index a6adb15a18..d77f04c9c5 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true # in case active_support/inflector is required without the rest of active_support -require_relative "inflector/inflections" -require_relative "inflector/transliterate" -require_relative "inflector/methods" +require "active_support/inflector/inflections" +require "active_support/inflector/transliterate" +require "active_support/inflector/methods" -require_relative "inflections" -require_relative "core_ext/string/inflections" +require "active_support/inflections" +require "active_support/core_ext/string/inflections" diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 36dfbc5f42..a639eddf10 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true require "concurrent/map" -require_relative "../core_ext/array/prepend_and_append" -require_relative "../core_ext/regexp" -require_relative "../i18n" +require "active_support/core_ext/array/prepend_and_append" +require "active_support/core_ext/regexp" +require "active_support/i18n" module ActiveSupport module Inflector diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 9398ed60a4..72de42b1c3 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../inflections" -require_relative "../core_ext/regexp" +require "active_support/inflections" +require "active_support/core_ext/regexp" module ActiveSupport # The Inflector transforms words from singular to plural, class names to table diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb index aa7b21734e..9801f1d118 100644 --- a/activesupport/lib/active_support/inflector/transliterate.rb +++ b/activesupport/lib/active_support/inflector/transliterate.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../core_ext/string/multibyte" -require_relative "../i18n" +require "active_support/core_ext/string/multibyte" +require "active_support/i18n" module ActiveSupport module Inflector diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index b5672025fb..d7887175c0 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true -require_relative "json/decoding" -require_relative "json/encoding" +require "active_support/json/decoding" +require "active_support/json/encoding" diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index caa4082dde..8c0e016dc5 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../core_ext/module/attribute_accessors" -require_relative "../core_ext/module/delegation" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/module/delegation" require "json" module ActiveSupport diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 4016d13364..1339c75ffe 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../core_ext/object/json" -require_relative "../core_ext/module/delegation" +require "active_support/core_ext/object/json" +require "active_support/core_ext/module/delegation" module ActiveSupport class << self diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 05a11221bf..0f7be06c8e 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "core_ext/module/attribute_accessors" -require_relative "core_ext/class/attribute" -require_relative "subscriber" +require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/class/attribute" +require "active_support/subscriber" module ActiveSupport # ActiveSupport::LogSubscriber is an object set to consume diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index 5b2abfc57c..3f19ef5009 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../log_subscriber" -require_relative "../logger" -require_relative "../notifications" +require "active_support/log_subscriber" +require "active_support/logger" +require "active_support/notifications" module ActiveSupport class LogSubscriber diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb index 3397ac4c9f..8152a182b4 100644 --- a/activesupport/lib/active_support/logger.rb +++ b/activesupport/lib/active_support/logger.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "logger_silence" -require_relative "logger_thread_safe_level" +require "active_support/logger_silence" +require "active_support/logger_thread_safe_level" require "logger" module ActiveSupport diff --git a/activesupport/lib/active_support/logger_silence.rb b/activesupport/lib/active_support/logger_silence.rb index 693c7a1947..89f32b6782 100644 --- a/activesupport/lib/active_support/logger_silence.rb +++ b/activesupport/lib/active_support/logger_silence.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "concern" -require_relative "core_ext/module/attribute_accessors" +require "active_support/concern" +require "active_support/core_ext/module/attribute_accessors" require "concurrent" module LoggerSilence diff --git a/activesupport/lib/active_support/logger_thread_safe_level.rb b/activesupport/lib/active_support/logger_thread_safe_level.rb index 3c7f53d92c..ba32813d3d 100644 --- a/activesupport/lib/active_support/logger_thread_safe_level.rb +++ b/activesupport/lib/active_support/logger_thread_safe_level.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "concern" +require "active_support/concern" module ActiveSupport module LoggerThreadSafeLevel # :nodoc: diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 0cc4f58453..69c95e0622 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -2,9 +2,9 @@ require "openssl" require "base64" -require_relative "core_ext/array/extract_options" -require_relative "message_verifier" -require_relative "messages/metadata" +require "active_support/core_ext/array/extract_options" +require "active_support/message_verifier" +require "active_support/messages/metadata" module ActiveSupport # MessageEncryptor is a simple way to encrypt values which get stored diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index f0b6503b96..622b66ee55 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true require "base64" -require_relative "core_ext/object/blank" -require_relative "security_utils" -require_relative "messages/metadata" -require_relative "messages/rotator" +require "active_support/core_ext/object/blank" +require "active_support/security_utils" +require "active_support/messages/metadata" +require "active_support/messages/rotator" module ActiveSupport # +MessageVerifier+ makes it easy to generate and verify messages which are diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index d676827e8e..8152b8fd22 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require_relative "../json" -require_relative "../core_ext/string/access" -require_relative "../core_ext/string/behavior" -require_relative "../core_ext/module/delegation" -require_relative "../core_ext/regexp" +require "active_support/json" +require "active_support/core_ext/string/access" +require "active_support/core_ext/string/behavior" +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/regexp" module ActiveSupport #:nodoc: module Multibyte #:nodoc: diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 96a3463905..6207de8094 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "notifications/instrumenter" -require_relative "notifications/fanout" -require_relative "per_thread_registry" +require "active_support/notifications/instrumenter" +require "active_support/notifications/fanout" +require "active_support/per_thread_registry" module ActiveSupport # = Notifications diff --git a/activesupport/lib/active_support/number_helper/number_converter.rb b/activesupport/lib/active_support/number_helper/number_converter.rb index 5ea9c8f113..06ba797a13 100644 --- a/activesupport/lib/active_support/number_helper/number_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_converter.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require_relative "../core_ext/big_decimal/conversions" -require_relative "../core_ext/object/blank" -require_relative "../core_ext/hash/keys" -require_relative "../i18n" -require_relative "../core_ext/class/attribute" +require "active_support/core_ext/big_decimal/conversions" +require "active_support/core_ext/object/blank" +require "active_support/core_ext/hash/keys" +require "active_support/i18n" +require "active_support/core_ext/class/attribute" module ActiveSupport module NumberHelper diff --git a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb index 1943b9e295..3f037c73ed 100644 --- a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../core_ext/numeric/inquiry" +require "active_support/core_ext/numeric/inquiry" module ActiveSupport module NumberHelper diff --git a/activesupport/lib/active_support/option_merger.rb b/activesupport/lib/active_support/option_merger.rb index 42cbbe7c42..ab9ca727f6 100644 --- a/activesupport/lib/active_support/option_merger.rb +++ b/activesupport/lib/active_support/option_merger.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "core_ext/hash/deep_merge" +require "active_support/core_ext/hash/deep_merge" module ActiveSupport class OptionMerger #:nodoc: diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index b74510fdb2..c4e419f546 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "core_ext/object/blank" +require "active_support/core_ext/object/blank" module ActiveSupport # Usually key value pairs are handled something like this: diff --git a/activesupport/lib/active_support/per_thread_registry.rb b/activesupport/lib/active_support/per_thread_registry.rb index dd0cc6a604..eb92fb4371 100644 --- a/activesupport/lib/active_support/per_thread_registry.rb +++ b/activesupport/lib/active_support/per_thread_registry.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "core_ext/module/delegation" +require "active_support/core_ext/module/delegation" module ActiveSupport # NOTE: This approach has been deprecated for end-user code in favor of {thread_mattr_accessor}[rdoc-ref:Module#thread_mattr_accessor] and friends. diff --git a/activesupport/lib/active_support/rails.rb b/activesupport/lib/active_support/rails.rb index fe82d1cc14..5c34a0abb3 100644 --- a/activesupport/lib/active_support/rails.rb +++ b/activesupport/lib/active_support/rails.rb @@ -11,25 +11,25 @@ # Rails and can change anytime. # Defines Object#blank? and Object#present?. -require_relative "core_ext/object/blank" +require "active_support/core_ext/object/blank" # Rails own autoload, eager_load, etc. -require_relative "dependencies/autoload" +require "active_support/dependencies/autoload" # Support for ClassMethods and the included macro. -require_relative "concern" +require "active_support/concern" # Defines Class#class_attribute. -require_relative "core_ext/class/attribute" +require "active_support/core_ext/class/attribute" # Defines Module#delegate. -require_relative "core_ext/module/delegation" +require "active_support/core_ext/module/delegation" # Defines ActiveSupport::Deprecation. -require_relative "deprecation" +require "active_support/deprecation" # Defines Regexp#match?. # # This should be removed when Rails needs Ruby 2.4 or later, and the require # added where other Regexp extensions are being used (easy to grep). -require_relative "core_ext/regexp" +require "active_support/core_ext/regexp" diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb index fe132ca7c6..8560eae110 100644 --- a/activesupport/lib/active_support/railtie.rb +++ b/activesupport/lib/active_support/railtie.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support" -require_relative "i18n_railtie" +require "active_support/i18n_railtie" module ActiveSupport class Railtie < Rails::Railtie # :nodoc: @@ -36,14 +36,14 @@ module ActiveSupport rescue TZInfo::DataSourceNotFound => e raise e.exception "tzinfo-data is not present. Please add gem 'tzinfo-data' to your Gemfile and run bundle install" end - require_relative "core_ext/time/zones" + require "active_support/core_ext/time/zones" Time.zone_default = Time.find_zone!(app.config.time_zone) end # Sets the default week start # If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised. initializer "active_support.initialize_beginning_of_week" do |app| - require_relative "core_ext/date/calculations" + require "active_support/core_ext/date/calculations" beginning_of_week_default = Date.find_beginning_of_week!(app.config.beginning_of_week) Date.beginning_of_week_default = beginning_of_week_default diff --git a/activesupport/lib/active_support/reloader.rb b/activesupport/lib/active_support/reloader.rb index 44062e3491..6cd11b1a26 100644 --- a/activesupport/lib/active_support/reloader.rb +++ b/activesupport/lib/active_support/reloader.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "execution_wrapper" +require "active_support/execution_wrapper" module ActiveSupport #-- diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb index 2f6aeef48a..e0fa29cacb 100644 --- a/activesupport/lib/active_support/rescuable.rb +++ b/activesupport/lib/active_support/rescuable.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "concern" -require_relative "core_ext/class/attribute" -require_relative "core_ext/string/inflections" +require "active_support/concern" +require "active_support/core_ext/class/attribute" +require "active_support/core_ext/string/inflections" module ActiveSupport # Rescuable module adds support for easier exception handling. diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb index 7913bb815e..d6dd5474d0 100644 --- a/activesupport/lib/active_support/subscriber.rb +++ b/activesupport/lib/active_support/subscriber.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "per_thread_registry" -require_relative "notifications" +require "active_support/per_thread_registry" +require "active_support/notifications" module ActiveSupport # ActiveSupport::Subscriber is an object set to consume diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb index fe13eaed4e..8561cba9f1 100644 --- a/activesupport/lib/active_support/tagged_logging.rb +++ b/activesupport/lib/active_support/tagged_logging.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require_relative "core_ext/module/delegation" -require_relative "core_ext/object/blank" +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/object/blank" require "logger" -require_relative "logger" +require "active_support/logger" module ActiveSupport # Wraps any standard Logger object to provide tagging capabilities. diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index bc42758f76..ee5286bd55 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -2,16 +2,16 @@ gem "minitest" # make sure we get the gem, not stdlib require "minitest" -require_relative "testing/tagged_logging" -require_relative "testing/setup_and_teardown" -require_relative "testing/assertions" -require_relative "testing/deprecation" -require_relative "testing/declarative" -require_relative "testing/isolation" -require_relative "testing/constant_lookup" -require_relative "testing/time_helpers" -require_relative "testing/file_fixtures" -require_relative "core_ext/kernel/reporting" +require "active_support/testing/tagged_logging" +require "active_support/testing/setup_and_teardown" +require "active_support/testing/assertions" +require "active_support/testing/deprecation" +require "active_support/testing/declarative" +require "active_support/testing/isolation" +require "active_support/testing/constant_lookup" +require "active_support/testing/time_helpers" +require "active_support/testing/file_fixtures" +require "active_support/core_ext/kernel/reporting" module ActiveSupport class TestCase < ::Minitest::Test diff --git a/activesupport/lib/active_support/testing/constant_lookup.rb b/activesupport/lib/active_support/testing/constant_lookup.rb index 0fedd486fb..51167e9237 100644 --- a/activesupport/lib/active_support/testing/constant_lookup.rb +++ b/activesupport/lib/active_support/testing/constant_lookup.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../concern" -require_relative "../inflector" +require "active_support/concern" +require "active_support/inflector" module ActiveSupport module Testing diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb index 4fda4832cc..f655435729 100644 --- a/activesupport/lib/active_support/testing/deprecation.rb +++ b/activesupport/lib/active_support/testing/deprecation.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../deprecation" -require_relative "../core_ext/regexp" +require "active_support/deprecation" +require "active_support/core_ext/regexp" module ActiveSupport module Testing diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index 18de7185d9..1dbf3c5da0 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../concern" -require_relative "../callbacks" +require "active_support/concern" +require "active_support/callbacks" module ActiveSupport module Testing diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb index fa5f46736c..8c620e7f8c 100644 --- a/activesupport/lib/active_support/testing/time_helpers.rb +++ b/activesupport/lib/active_support/testing/time_helpers.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../core_ext/string/strip" # for strip_heredoc -require_relative "../core_ext/time/calculations" +require "active_support/core_ext/string/strip" # for strip_heredoc +require "active_support/core_ext/time/calculations" require "concurrent/map" module ActiveSupport diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb index fb1c5cee1c..51854675bf 100644 --- a/activesupport/lib/active_support/time.rb +++ b/activesupport/lib/active_support/time.rb @@ -9,12 +9,12 @@ end require "date" require "time" -require_relative "core_ext/time" -require_relative "core_ext/date" -require_relative "core_ext/date_time" +require "active_support/core_ext/time" +require "active_support/core_ext/date" +require "active_support/core_ext/date_time" -require_relative "core_ext/integer/time" -require_relative "core_ext/numeric/time" +require "active_support/core_ext/integer/time" +require "active_support/core_ext/numeric/time" -require_relative "core_ext/string/conversions" -require_relative "core_ext/string/zones" +require "active_support/core_ext/string/conversions" +require "active_support/core_ext/string/zones" diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 59cafa193e..20650ce714 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require_relative "duration" -require_relative "values/time_zone" -require_relative "core_ext/object/acts_like" -require_relative "core_ext/date_and_time/compatibility" +require "active_support/duration" +require "active_support/values/time_zone" +require "active_support/core_ext/object/acts_like" +require "active_support/core_ext/date_and_time/compatibility" module ActiveSupport # A Time-like class that can represent a time in any time zone. Necessary diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index c871f11422..4d1fbd4453 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -2,7 +2,7 @@ require "tzinfo" require "concurrent/map" -require_relative "../core_ext/object/blank" +require "active_support/core_ext/object/blank" module ActiveSupport # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index d49ee4508e..ee2048cb54 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -3,9 +3,9 @@ require "time" require "base64" require "bigdecimal" -require_relative "core_ext/module/delegation" -require_relative "core_ext/string/inflections" -require_relative "core_ext/date_time/calculations" +require "active_support/core_ext/module/delegation" +require "active_support/core_ext/string/inflections" +require "active_support/core_ext/date_time/calculations" module ActiveSupport # = XmlMini @@ -199,7 +199,7 @@ module ActiveSupport if name.is_a?(Module) name else - require_relative "xml_mini/#{name.downcase}" + require "active_support/xml_mini/#{name.downcase}" ActiveSupport.const_get("XmlMini_#{name}") end end diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index ecfe389f10..7f94a64016 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -5,7 +5,7 @@ raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFO require "jruby" include Java -require_relative "../core_ext/object/blank" +require "active_support/core_ext/object/blank" java_import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder java_import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index b1be38fadf..0b000fea60 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "libxml" -require_relative "../core_ext/object/blank" +require "active_support/core_ext/object/blank" require "stringio" module ActiveSupport diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb index 3ad494310d..dcf16e6084 100644 --- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb +++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "libxml" -require_relative "../core_ext/object/blank" +require "active_support/core_ext/object/blank" require "stringio" module ActiveSupport diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 1987330c25..5ee6fc8159 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -6,7 +6,7 @@ rescue LoadError => e $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install" raise e end -require_relative "../core_ext/object/blank" +require "active_support/core_ext/object/blank" require "stringio" module ActiveSupport diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index fb57eb8867..b01ed00a14 100644 --- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -6,7 +6,7 @@ rescue LoadError => e $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install" raise e end -require_relative "../core_ext/object/blank" +require "active_support/core_ext/object/blank" require "stringio" module ActiveSupport diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index 5fd8978fc0..32458d5b0d 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../core_ext/kernel/reporting" -require_relative "../core_ext/object/blank" +require "active_support/core_ext/kernel/reporting" +require "active_support/core_ext/object/blank" require "stringio" module ActiveSupport -- cgit v1.2.3 From 8c2ba84d6301b429bd6d68d7b8fd24f86926dabd Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:15:16 +0900 Subject: [Active Model] require => require_relative This basically reverts ee5cfc01a5797f854c8441539b0cae326a81b963 --- activemodel/lib/active_model.rb | 2 +- activemodel/lib/active_model/type.rb | 32 +++++++++++----------- activemodel/lib/active_model/type/big_integer.rb | 2 +- activemodel/lib/active_model/type/helpers.rb | 8 +++--- activemodel/lib/active_model/type/string.rb | 2 +- .../lib/active_model/validations/exclusion.rb | 2 +- .../lib/active_model/validations/inclusion.rb | 2 +- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index dfd9be34a9..a97d7989be 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -25,7 +25,7 @@ require "active_support" require "active_support/rails" -require_relative "active_model/version" +require "active_model/version" module ActiveModel extend ActiveSupport::Autoload diff --git a/activemodel/lib/active_model/type.rb b/activemodel/lib/active_model/type.rb index cb603b3d25..b0ed67f28e 100644 --- a/activemodel/lib/active_model/type.rb +++ b/activemodel/lib/active_model/type.rb @@ -1,21 +1,21 @@ # frozen_string_literal: true -require_relative "type/helpers" -require_relative "type/value" - -require_relative "type/big_integer" -require_relative "type/binary" -require_relative "type/boolean" -require_relative "type/date" -require_relative "type/date_time" -require_relative "type/decimal" -require_relative "type/float" -require_relative "type/immutable_string" -require_relative "type/integer" -require_relative "type/string" -require_relative "type/time" - -require_relative "type/registry" +require "active_model/type/helpers" +require "active_model/type/value" + +require "active_model/type/big_integer" +require "active_model/type/binary" +require "active_model/type/boolean" +require "active_model/type/date" +require "active_model/type/date_time" +require "active_model/type/decimal" +require "active_model/type/float" +require "active_model/type/immutable_string" +require "active_model/type/integer" +require "active_model/type/string" +require "active_model/type/time" + +require "active_model/type/registry" module ActiveModel module Type diff --git a/activemodel/lib/active_model/type/big_integer.rb b/activemodel/lib/active_model/type/big_integer.rb index d080fcc0f2..89e43bcc5f 100644 --- a/activemodel/lib/active_model/type/big_integer.rb +++ b/activemodel/lib/active_model/type/big_integer.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "integer" +require "active_model/type/integer" module ActiveModel module Type diff --git a/activemodel/lib/active_model/type/helpers.rb b/activemodel/lib/active_model/type/helpers.rb index a4e1427b64..403f0a9e6b 100644 --- a/activemodel/lib/active_model/type/helpers.rb +++ b/activemodel/lib/active_model/type/helpers.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "helpers/accepts_multiparameter_time" -require_relative "helpers/numeric" -require_relative "helpers/mutable" -require_relative "helpers/time_value" +require "active_model/type/helpers/accepts_multiparameter_time" +require "active_model/type/helpers/numeric" +require "active_model/type/helpers/mutable" +require "active_model/type/helpers/time_value" diff --git a/activemodel/lib/active_model/type/string.rb b/activemodel/lib/active_model/type/string.rb index 6ba2c2a3d2..36f13945b1 100644 --- a/activemodel/lib/active_model/type/string.rb +++ b/activemodel/lib/active_model/type/string.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "immutable_string" +require "active_model/type/immutable_string" module ActiveModel module Type diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb index a6cbfcc813..3be7ab6ba8 100644 --- a/activemodel/lib/active_model/validations/exclusion.rb +++ b/activemodel/lib/active_model/validations/exclusion.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "clusivity" +require "active_model/validations/clusivity" module ActiveModel module Validations diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb index 00e27b528a..3104e7e329 100644 --- a/activemodel/lib/active_model/validations/inclusion.rb +++ b/activemodel/lib/active_model/validations/inclusion.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "clusivity" +require "active_model/validations/clusivity" module ActiveModel module Validations -- cgit v1.2.3 From d5ea33b9a003d32ebaeae7ad89c479c04f790642 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:17:03 +0900 Subject: [Active Record] require => require_relative This basically reverts 9d4f79d3d394edb74fa2192e5d9ad7b09ce50c6d --- activerecord/lib/active_record.rb | 4 +-- activerecord/lib/active_record/associations.rb | 2 +- .../associations/builder/collection_association.rb | 2 +- .../join_dependency/join_association.rb | 2 +- .../associations/join_dependency/join_base.rb | 2 +- .../attribute/user_provided_default.rb | 2 +- .../lib/active_record/attribute_methods/dirty.rb | 2 +- activerecord/lib/active_record/attribute_set.rb | 4 +-- .../lib/active_record/attribute_set/builder.rb | 2 +- activerecord/lib/active_record/attributes.rb | 2 +- activerecord/lib/active_record/base.rb | 16 ++++----- .../abstract/schema_statements.rb | 2 +- .../connection_adapters/abstract_adapter.rb | 12 +++---- .../connection_adapters/abstract_mysql_adapter.rb | 20 +++++------ .../connection_adapters/mysql2_adapter.rb | 4 +-- .../connection_adapters/postgresql/oid.rb | 42 +++++++++++----------- .../connection_adapters/postgresql_adapter.rb | 28 +++++++-------- .../connection_adapters/sqlite3_adapter.rb | 16 ++++----- activerecord/lib/active_record/explain.rb | 2 +- .../lib/active_record/explain_subscriber.rb | 2 +- activerecord/lib/active_record/fixtures.rb | 4 +-- .../lib/active_record/internal_metadata.rb | 4 +-- activerecord/lib/active_record/railtie.rb | 10 +++--- .../active_record/railties/controller_runtime.rb | 2 +- .../lib/active_record/railties/databases.rake | 6 ++-- activerecord/lib/active_record/relation/batches.rb | 2 +- .../active_record/relation/predicate_builder.rb | 16 ++++----- .../lib/active_record/relation/query_attribute.rb | 2 +- .../lib/active_record/relation/query_methods.rb | 8 ++--- .../lib/active_record/relation/spawn_methods.rb | 2 +- activerecord/lib/active_record/schema_migration.rb | 4 +-- .../active_record/tasks/mysql_database_tasks.rb | 2 +- activerecord/lib/active_record/type.rb | 24 ++++++------- activerecord/lib/active_record/type_caster.rb | 4 +-- activerecord/lib/active_record/validations.rb | 10 +++--- 35 files changed, 134 insertions(+), 134 deletions(-) diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 0c19fed9e1..bf6dfd46e1 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -28,8 +28,8 @@ require "active_support/rails" require "active_model" require "arel" -require_relative "active_record/version" -require_relative "active_record/attribute_set" +require "active_record/version" +require "active_record/attribute_set" module ActiveRecord extend ActiveSupport::Autoload diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 91f915183a..2723b3dc03 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -3,7 +3,7 @@ require "active_support/core_ext/enumerable" require "active_support/core_ext/string/conversions" require "active_support/core_ext/module/remove_method" -require_relative "errors" +require "active_record/errors" module ActiveRecord class AssociationNotFoundError < ConfigurationError #:nodoc: diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index 753fde5146..35a72c3850 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../associations" +require "active_record/associations" module ActiveRecord::Associations::Builder # :nodoc: class CollectionAssociation < Association #:nodoc: diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index a007d0cf5f..221c791bf8 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "join_part" +require "active_record/associations/join_dependency/join_part" module ActiveRecord module Associations diff --git a/activerecord/lib/active_record/associations/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/join_dependency/join_base.rb index 8a8fa8993b..988b4e8fa2 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_base.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_base.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "join_part" +require "active_record/associations/join_dependency/join_part" module ActiveRecord module Associations diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb index 690a931615..f746960fac 100644 --- a/activerecord/lib/active_record/attribute/user_provided_default.rb +++ b/activerecord/lib/active_record/attribute/user_provided_default.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../attribute" +require "active_record/attribute" module ActiveRecord class Attribute # :nodoc: diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 06598439d8..79110d04f4 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/module/attribute_accessors" -require_relative "../attribute_mutation_tracker" +require "active_record/attribute_mutation_tracker" module ActiveRecord module AttributeMethods diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb index 492067e2b3..9785666e77 100644 --- a/activerecord/lib/active_record/attribute_set.rb +++ b/activerecord/lib/active_record/attribute_set.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "attribute_set/builder" -require_relative "attribute_set/yaml_encoder" +require "active_record/attribute_set/builder" +require "active_record/attribute_set/yaml_encoder" module ActiveRecord class AttributeSet # :nodoc: diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index e3a9c7fdb3..349cc7e403 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../attribute" +require "active_record/attribute" module ActiveRecord class AttributeSet # :nodoc: diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index afb559db71..9224d58928 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "attribute/user_provided_default" +require "active_record/attribute/user_provided_default" module ActiveRecord # See ActiveRecord::Attributes::ClassMethods for documentation diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 541ff51fbe..b7ad944cec 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -15,14 +15,14 @@ require "active_support/core_ext/kernel/singleton_class" require "active_support/core_ext/module/introspection" require "active_support/core_ext/object/duplicable" require "active_support/core_ext/class/subclasses" -require_relative "attribute_decorators" -require_relative "define_callbacks" -require_relative "errors" -require_relative "log_subscriber" -require_relative "explain_subscriber" -require_relative "relation/delegation" -require_relative "attributes" -require_relative "type_caster" +require "active_record/attribute_decorators" +require "active_record/define_callbacks" +require "active_record/errors" +require "active_record/log_subscriber" +require "active_record/explain_subscriber" +require "active_record/relation/delegation" +require "active_record/attributes" +require "active_record/type_caster" module ActiveRecord #:nodoc: # = Active Record 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 e6bcdbda60..24b2234c67 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../../migration/join_table" +require "active_record/migration/join_table" require "active_support/core_ext/string/access" require "digest/sha2" diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 6859feb2f3..f2fe4e2c30 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require_relative "../type" -require_relative "determine_if_preparable_visitor" -require_relative "schema_cache" -require_relative "sql_type_metadata" -require_relative "abstract/schema_dumper" -require_relative "abstract/schema_creation" +require "active_record/type" +require "active_record/connection_adapters/determine_if_preparable_visitor" +require "active_record/connection_adapters/schema_cache" +require "active_record/connection_adapters/sql_type_metadata" +require "active_record/connection_adapters/abstract/schema_dumper" +require "active_record/connection_adapters/abstract/schema_creation" require "arel/collectors/bind" require "arel/collectors/composite" require "arel/collectors/sql_string" 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 add6f8632b..bfec6fb784 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require_relative "abstract_adapter" -require_relative "statement_pool" -require_relative "mysql/column" -require_relative "mysql/explain_pretty_printer" -require_relative "mysql/quoting" -require_relative "mysql/schema_creation" -require_relative "mysql/schema_definitions" -require_relative "mysql/schema_dumper" -require_relative "mysql/schema_statements" -require_relative "mysql/type_metadata" +require "active_record/connection_adapters/abstract_adapter" +require "active_record/connection_adapters/statement_pool" +require "active_record/connection_adapters/mysql/column" +require "active_record/connection_adapters/mysql/explain_pretty_printer" +require "active_record/connection_adapters/mysql/quoting" +require "active_record/connection_adapters/mysql/schema_creation" +require "active_record/connection_adapters/mysql/schema_definitions" +require "active_record/connection_adapters/mysql/schema_dumper" +require "active_record/connection_adapters/mysql/schema_statements" +require "active_record/connection_adapters/mysql/type_metadata" require "active_support/core_ext/string/strip" diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 2c2321872d..8de582fee1 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "abstract_mysql_adapter" -require_relative "mysql/database_statements" +require "active_record/connection_adapters/abstract_mysql_adapter" +require "active_record/connection_adapters/mysql/database_statements" gem "mysql2", ">= 0.3.18", "< 0.5" require "mysql2" diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index b28418d74f..542ca75d3e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -1,27 +1,27 @@ # frozen_string_literal: true -require_relative "oid/array" -require_relative "oid/bit" -require_relative "oid/bit_varying" -require_relative "oid/bytea" -require_relative "oid/cidr" -require_relative "oid/date_time" -require_relative "oid/decimal" -require_relative "oid/enum" -require_relative "oid/hstore" -require_relative "oid/inet" -require_relative "oid/jsonb" -require_relative "oid/money" -require_relative "oid/oid" -require_relative "oid/point" -require_relative "oid/legacy_point" -require_relative "oid/range" -require_relative "oid/specialized_string" -require_relative "oid/uuid" -require_relative "oid/vector" -require_relative "oid/xml" +require "active_record/connection_adapters/postgresql/oid/array" +require "active_record/connection_adapters/postgresql/oid/bit" +require "active_record/connection_adapters/postgresql/oid/bit_varying" +require "active_record/connection_adapters/postgresql/oid/bytea" +require "active_record/connection_adapters/postgresql/oid/cidr" +require "active_record/connection_adapters/postgresql/oid/date_time" +require "active_record/connection_adapters/postgresql/oid/decimal" +require "active_record/connection_adapters/postgresql/oid/enum" +require "active_record/connection_adapters/postgresql/oid/hstore" +require "active_record/connection_adapters/postgresql/oid/inet" +require "active_record/connection_adapters/postgresql/oid/jsonb" +require "active_record/connection_adapters/postgresql/oid/money" +require "active_record/connection_adapters/postgresql/oid/oid" +require "active_record/connection_adapters/postgresql/oid/point" +require "active_record/connection_adapters/postgresql/oid/legacy_point" +require "active_record/connection_adapters/postgresql/oid/range" +require "active_record/connection_adapters/postgresql/oid/specialized_string" +require "active_record/connection_adapters/postgresql/oid/uuid" +require "active_record/connection_adapters/postgresql/oid/vector" +require "active_record/connection_adapters/postgresql/oid/xml" -require_relative "oid/type_map_initializer" +require "active_record/connection_adapters/postgresql/oid/type_map_initializer" module ActiveRecord module ConnectionAdapters diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index eae3d59946..e9ae861bfb 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -4,20 +4,20 @@ gem "pg", "~> 0.18" require "pg" -require_relative "abstract_adapter" -require_relative "statement_pool" -require_relative "postgresql/column" -require_relative "postgresql/database_statements" -require_relative "postgresql/explain_pretty_printer" -require_relative "postgresql/oid" -require_relative "postgresql/quoting" -require_relative "postgresql/referential_integrity" -require_relative "postgresql/schema_creation" -require_relative "postgresql/schema_definitions" -require_relative "postgresql/schema_dumper" -require_relative "postgresql/schema_statements" -require_relative "postgresql/type_metadata" -require_relative "postgresql/utils" +require "active_record/connection_adapters/abstract_adapter" +require "active_record/connection_adapters/statement_pool" +require "active_record/connection_adapters/postgresql/column" +require "active_record/connection_adapters/postgresql/database_statements" +require "active_record/connection_adapters/postgresql/explain_pretty_printer" +require "active_record/connection_adapters/postgresql/oid" +require "active_record/connection_adapters/postgresql/quoting" +require "active_record/connection_adapters/postgresql/referential_integrity" +require "active_record/connection_adapters/postgresql/schema_creation" +require "active_record/connection_adapters/postgresql/schema_definitions" +require "active_record/connection_adapters/postgresql/schema_dumper" +require "active_record/connection_adapters/postgresql/schema_statements" +require "active_record/connection_adapters/postgresql/type_metadata" +require "active_record/connection_adapters/postgresql/utils" module ActiveRecord module ConnectionHandling # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 6fdd666486..670afa3684 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -require_relative "abstract_adapter" -require_relative "statement_pool" -require_relative "sqlite3/explain_pretty_printer" -require_relative "sqlite3/quoting" -require_relative "sqlite3/schema_creation" -require_relative "sqlite3/schema_definitions" -require_relative "sqlite3/schema_dumper" -require_relative "sqlite3/schema_statements" +require "active_record/connection_adapters/abstract_adapter" +require "active_record/connection_adapters/statement_pool" +require "active_record/connection_adapters/sqlite3/explain_pretty_printer" +require "active_record/connection_adapters/sqlite3/quoting" +require "active_record/connection_adapters/sqlite3/schema_creation" +require "active_record/connection_adapters/sqlite3/schema_definitions" +require "active_record/connection_adapters/sqlite3/schema_dumper" +require "active_record/connection_adapters/sqlite3/schema_statements" gem "sqlite3", "~> 1.3.6" require "sqlite3" diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb index 8bb54a24b7..7ccb938888 100644 --- a/activerecord/lib/active_record/explain.rb +++ b/activerecord/lib/active_record/explain.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "explain_registry" +require "active_record/explain_registry" module ActiveRecord module Explain diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb index 9252fa3fed..a86217abc0 100644 --- a/activerecord/lib/active_record/explain_subscriber.rb +++ b/activerecord/lib/active_record/explain_subscriber.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/notifications" -require_relative "explain_registry" +require "active_record/explain_registry" module ActiveRecord class ExplainSubscriber # :nodoc: diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 12169fffa9..86f13d75d5 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -6,8 +6,8 @@ require "zlib" require "set" require "active_support/dependencies" require "active_support/core_ext/digest/uuid" -require_relative "fixture_set/file" -require_relative "errors" +require "active_record/fixture_set/file" +require "active_record/errors" module ActiveRecord class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc: diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb index 14795cc815..5a65edf27e 100644 --- a/activerecord/lib/active_record/internal_metadata.rb +++ b/activerecord/lib/active_record/internal_metadata.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "scoping/default" -require_relative "scoping/named" +require "active_record/scoping/default" +require "active_record/scoping/named" module ActiveRecord # This class is used to create a table that keeps track of values and keys such diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index ead42d64ec..812e1d7a00 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -53,8 +53,8 @@ module ActiveRecord # to avoid cross references when loading a constant for the # first time. Also, make it output to STDERR. console do |app| - require_relative "railties/console_sandbox" if app.sandbox? - require_relative "base" + require "active_record/railties/console_sandbox" if app.sandbox? + require "active_record/base" unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT) console = ActiveSupport::Logger.new(STDERR) Rails.logger.extend ActiveSupport::Logger.broadcast console @@ -62,7 +62,7 @@ module ActiveRecord end runner do - require_relative "base" + require "active_record/base" end initializer "active_record.initialize_timezone" do @@ -106,7 +106,7 @@ module ActiveRecord initializer "active_record.warn_on_records_fetched_greater_than" do if config.active_record.warn_on_records_fetched_greater_than ActiveSupport.on_load(:active_record) do - require_relative "relation/record_fetch_warning" + require "active_record/relation/record_fetch_warning" end end end @@ -146,7 +146,7 @@ end_warning # Expose database runtime to controller for logging. initializer "active_record.log_runtime" do - require_relative "railties/controller_runtime" + require "active_record/railties/controller_runtime" ActiveSupport.on_load(:action_controller) do include ActiveRecord::Railties::ControllerRuntime end diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index 3cf66980a5..2ae733f657 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/module/attr_internal" -require_relative "../log_subscriber" +require "active_record/log_subscriber" module ActiveRecord module Railties # :nodoc: diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index eb32c29b7e..723272b4b2 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -189,7 +189,7 @@ db_namespace = namespace :db do namespace :fixtures do desc "Loads fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." task load: [:environment, :load_config] do - require_relative "../fixtures" + require "active_record/fixtures" base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path @@ -211,7 +211,7 @@ db_namespace = namespace :db do # desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." task identify: [:environment, :load_config] do - require_relative "../fixtures" + require "active_record/fixtures" label, id = ENV["LABEL"], ENV["ID"] raise "LABEL or ID required" if label.blank? && id.blank? @@ -237,7 +237,7 @@ db_namespace = namespace :db do namespace :schema do desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record" task dump: [:environment, :load_config] do - require_relative "../schema_dumper" + require "active_record/schema_dumper" filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema.rb") File.open(filename, "w:utf-8") do |file| ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 356ad0dcd6..561869017a 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "batches/batch_enumerator" +require "active_record/relation/batches/batch_enumerator" module ActiveRecord module Batches diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index be4b169f67..885c26d7aa 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -124,11 +124,11 @@ module ActiveRecord end end -require_relative "predicate_builder/array_handler" -require_relative "predicate_builder/base_handler" -require_relative "predicate_builder/basic_object_handler" -require_relative "predicate_builder/range_handler" -require_relative "predicate_builder/relation_handler" - -require_relative "predicate_builder/association_query_value" -require_relative "predicate_builder/polymorphic_array_value" +require "active_record/relation/predicate_builder/array_handler" +require "active_record/relation/predicate_builder/base_handler" +require "active_record/relation/predicate_builder/basic_object_handler" +require "active_record/relation/predicate_builder/range_handler" +require "active_record/relation/predicate_builder/relation_handler" + +require "active_record/relation/predicate_builder/association_query_value" +require "active_record/relation/predicate_builder/polymorphic_array_value" diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb index 5a9a7fd432..fad08e2613 100644 --- a/activerecord/lib/active_record/relation/query_attribute.rb +++ b/activerecord/lib/active_record/relation/query_attribute.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../attribute" +require "active_record/attribute" module ActiveRecord class Relation diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 578d8a6eb7..269790cdba 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require_relative "from_clause" -require_relative "query_attribute" -require_relative "where_clause" -require_relative "where_clause_factory" +require "active_record/relation/from_clause" +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" module ActiveRecord diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 424894f835..617d8de8b2 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -2,7 +2,7 @@ require "active_support/core_ext/hash/except" require "active_support/core_ext/hash/slice" -require_relative "merger" +require "active_record/relation/merger" module ActiveRecord module SpawnMethods diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb index 339a5334a8..f2d8b038fa 100644 --- a/activerecord/lib/active_record/schema_migration.rb +++ b/activerecord/lib/active_record/schema_migration.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "scoping/default" -require_relative "scoping/named" +require "active_record/scoping/default" +require "active_record/scoping/named" module ActiveRecord # This class is used to create a table that keeps track of which migrations diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index 84265aa9e3..2f91884f59 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -101,7 +101,7 @@ module ActiveRecord def error_class if configuration["adapter"].include?("jdbc") - require_relative "../railties/jdbcmysql_error" + require "active_record/railties/jdbcmysql_error" ArJdbcMySQL::Error elsif defined?(Mysql2) Mysql2::Error diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb index fa22df92b8..c303186ef2 100644 --- a/activerecord/lib/active_record/type.rb +++ b/activerecord/lib/active_record/type.rb @@ -2,21 +2,21 @@ require "active_model/type" -require_relative "type/internal/timezone" +require "active_record/type/internal/timezone" -require_relative "type/date" -require_relative "type/date_time" -require_relative "type/decimal_without_scale" -require_relative "type/json" -require_relative "type/time" -require_relative "type/text" -require_relative "type/unsigned_integer" +require "active_record/type/date" +require "active_record/type/date_time" +require "active_record/type/decimal_without_scale" +require "active_record/type/json" +require "active_record/type/time" +require "active_record/type/text" +require "active_record/type/unsigned_integer" -require_relative "type/serialized" -require_relative "type/adapter_specific_registry" +require "active_record/type/serialized" +require "active_record/type/adapter_specific_registry" -require_relative "type/type_map" -require_relative "type/hash_lookup_type_map" +require "active_record/type/type_map" +require "active_record/type/hash_lookup_type_map" module ActiveRecord module Type diff --git a/activerecord/lib/active_record/type_caster.rb b/activerecord/lib/active_record/type_caster.rb index ed2e4fb79c..2e5f45fa3d 100644 --- a/activerecord/lib/active_record/type_caster.rb +++ b/activerecord/lib/active_record/type_caster.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "type_caster/map" -require_relative "type_caster/connection" +require "active_record/type_caster/map" +require "active_record/type_caster/connection" module ActiveRecord module TypeCaster # :nodoc: diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 3f5c879f2f..ca27a3f0ab 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -86,8 +86,8 @@ module ActiveRecord end end -require_relative "validations/associated" -require_relative "validations/uniqueness" -require_relative "validations/presence" -require_relative "validations/absence" -require_relative "validations/length" +require "active_record/validations/associated" +require "active_record/validations/uniqueness" +require "active_record/validations/presence" +require "active_record/validations/absence" +require "active_record/validations/length" -- cgit v1.2.3 From b0d0c9f40df3bee73d3f36005238d7d0227579d2 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:18:17 +0900 Subject: [Action Pack] require => require_relative This basically reverts e9fca7668b9eba82bcc832cb0061459703368397, d08da958b9ae17d4bbe4c9d7db497ece2450db5f, d1fe1dcf8ab1c0210a37c2a78c1ee52cf199a66d, and 68eaf7b4d5f2bb56d939f71c5ece2d61cf6680a3 --- actionpack/lib/abstract_controller/base.rb | 2 +- actionpack/lib/abstract_controller/rendering.rb | 2 +- actionpack/lib/action_controller.rb | 4 ++-- actionpack/lib/action_controller/api.rb | 2 +- actionpack/lib/action_controller/base.rb | 4 ++-- actionpack/lib/action_controller/metal/data_streaming.rb | 2 +- .../action_controller/metal/request_forgery_protection.rb | 2 +- actionpack/lib/action_controller/railtie.rb | 2 +- actionpack/lib/action_controller/test_case.rb | 2 +- actionpack/lib/action_dispatch/http/filter_parameters.rb | 2 +- actionpack/lib/action_dispatch/http/mime_type.rb | 2 +- actionpack/lib/action_dispatch/http/request.rb | 14 +++++++------- actionpack/lib/action_dispatch/http/response.rb | 4 ++-- actionpack/lib/action_dispatch/journey.rb | 10 +++++----- actionpack/lib/action_dispatch/journey/gtg/builder.rb | 2 +- .../lib/action_dispatch/journey/gtg/transition_table.rb | 2 +- actionpack/lib/action_dispatch/journey/nfa/builder.rb | 4 ++-- .../lib/action_dispatch/journey/nfa/transition_table.rb | 2 +- actionpack/lib/action_dispatch/journey/nodes/node.rb | 2 +- actionpack/lib/action_dispatch/journey/parser.rb | 2 +- actionpack/lib/action_dispatch/journey/parser.y | 2 +- actionpack/lib/action_dispatch/journey/parser_extras.rb | 4 ++-- actionpack/lib/action_dispatch/journey/router.rb | 12 ++++++------ .../lib/action_dispatch/middleware/debug_exceptions.rb | 6 +++--- .../action_dispatch/middleware/session/abstract_store.rb | 4 ++-- .../lib/action_dispatch/middleware/session/cache_store.rb | 2 +- .../lib/action_dispatch/middleware/session/cookie_store.rb | 2 +- .../action_dispatch/middleware/session/mem_cache_store.rb | 2 +- .../lib/action_dispatch/middleware/show_exceptions.rb | 4 ++-- actionpack/lib/action_dispatch/routing/mapper.rb | 4 ++-- actionpack/lib/action_dispatch/routing/redirection.rb | 4 ++-- actionpack/lib/action_dispatch/routing/route_set.rb | 6 +++--- actionpack/lib/action_dispatch/system_test_case.rb | 10 +++++----- actionpack/lib/action_dispatch/testing/integration.rb | 2 +- actionpack/lib/action_dispatch/testing/test_process.rb | 4 ++-- actionpack/lib/action_dispatch/testing/test_response.rb | 2 +- actionpack/lib/action_pack.rb | 2 +- 37 files changed, 70 insertions(+), 70 deletions(-) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 14afb355ab..a312af6715 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "error" +require "abstract_controller/error" require "active_support/configurable" require "active_support/descendants_tracker" require "active_support/core_ext/module/anonymous" diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index fe1cd4a5a5..8ba2b25552 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "error" +require "abstract_controller/error" require "action_view" require "action_view/view_paths" require "set" diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index e893507baa..bd19b8cd5d 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -3,8 +3,8 @@ require "active_support/rails" require "abstract_controller" require "action_dispatch" -require_relative "action_controller/metal/live" -require_relative "action_controller/metal/strong_parameters" +require "action_controller/metal/live" +require "action_controller/metal/strong_parameters" module ActionController extend ActiveSupport::Autoload diff --git a/actionpack/lib/action_controller/api.rb b/actionpack/lib/action_controller/api.rb index ba9af4767e..b192e496de 100644 --- a/actionpack/lib/action_controller/api.rb +++ b/actionpack/lib/action_controller/api.rb @@ -2,7 +2,7 @@ require "action_view" require "action_controller" -require_relative "log_subscriber" +require "action_controller/log_subscriber" module ActionController # API Controller is a lightweight version of ActionController::Base, diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index bbc48e6eb7..b73269871b 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require "action_view" -require_relative "log_subscriber" -require_relative "metal/params_wrapper" +require "action_controller/log_subscriber" +require "action_controller/metal/params_wrapper" module ActionController # Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb index 882f6f3d0a..5a82ccf668 100644 --- a/actionpack/lib/action_controller/metal/data_streaming.rb +++ b/actionpack/lib/action_controller/metal/data_streaming.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "exceptions" +require "action_controller/metal/exceptions" module ActionController #:nodoc: # Methods for sending arbitrary data and for streaming files to the browser, diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 813a7e00d4..d6cd5fd9e0 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "rack/session/abstract/id" -require_relative "exceptions" +require "action_controller/metal/exceptions" require "active_support/security_utils" module ActionController #:nodoc: diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index 769be39004..7d42f5d931 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -4,7 +4,7 @@ require "rails" require "action_controller" require "action_dispatch/railtie" require "abstract_controller/railties/routes_helpers" -require_relative "railties/helpers" +require "action_controller/railties/helpers" require "action_view/railtie" module ActionController diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 74d557fc18..4b408750a4 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -7,7 +7,7 @@ require "active_support/core_ext/module/anonymous" require "active_support/core_ext/module/redefine_method" require "active_support/core_ext/hash/keys" require "active_support/testing/constant_lookup" -require_relative "template_assertions" +require "action_controller/template_assertions" require "rails-dom-testing" module ActionController diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index b7141cc1b9..41a47f2c82 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "parameter_filter" +require "action_dispatch/http/parameter_filter" module ActionDispatch module Http diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index d797e90e52..d2b2106845 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -339,4 +339,4 @@ module Mime end end -require_relative "mime_types" +require "action_dispatch/http/mime_types" diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 5c172aecad..60aa1d4e8a 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -3,15 +3,15 @@ require "stringio" require "active_support/inflector" -require_relative "headers" +require "action_dispatch/http/headers" require "action_controller/metal/exceptions" require "rack/request" -require_relative "cache" -require_relative "mime_negotiation" -require_relative "parameters" -require_relative "filter_parameters" -require_relative "upload" -require_relative "url" +require "action_dispatch/http/cache" +require "action_dispatch/http/mime_negotiation" +require "action_dispatch/http/parameters" +require "action_dispatch/http/filter_parameters" +require "action_dispatch/http/upload" +require "action_dispatch/http/url" require "active_support/core_ext/array/conversions" module ActionDispatch diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 0c7b153420..7e50cb6d23 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require "active_support/core_ext/module/attribute_accessors" -require_relative "filter_redirect" -require_relative "cache" +require "action_dispatch/http/filter_redirect" +require "action_dispatch/http/cache" require "monitor" module ActionDispatch # :nodoc: diff --git a/actionpack/lib/action_dispatch/journey.rb b/actionpack/lib/action_dispatch/journey.rb index 903063d00f..2852efa6ae 100644 --- a/actionpack/lib/action_dispatch/journey.rb +++ b/actionpack/lib/action_dispatch/journey.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "journey/router" -require_relative "journey/gtg/builder" -require_relative "journey/gtg/simulator" -require_relative "journey/nfa/builder" -require_relative "journey/nfa/simulator" +require "action_dispatch/journey/router" +require "action_dispatch/journey/gtg/builder" +require "action_dispatch/journey/gtg/simulator" +require "action_dispatch/journey/nfa/builder" +require "action_dispatch/journey/nfa/simulator" diff --git a/actionpack/lib/action_dispatch/journey/gtg/builder.rb b/actionpack/lib/action_dispatch/journey/gtg/builder.rb index 7e3d957baa..44c31053cb 100644 --- a/actionpack/lib/action_dispatch/journey/gtg/builder.rb +++ b/actionpack/lib/action_dispatch/journey/gtg/builder.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "transition_table" +require "action_dispatch/journey/gtg/transition_table" module ActionDispatch module Journey # :nodoc: diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb index 6ed478f816..ea647e051a 100644 --- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb +++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../nfa/dot" +require "action_dispatch/journey/nfa/dot" module ActionDispatch module Journey # :nodoc: diff --git a/actionpack/lib/action_dispatch/journey/nfa/builder.rb b/actionpack/lib/action_dispatch/journey/nfa/builder.rb index 3135c05ffa..d22302e101 100644 --- a/actionpack/lib/action_dispatch/journey/nfa/builder.rb +++ b/actionpack/lib/action_dispatch/journey/nfa/builder.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "transition_table" -require_relative "../gtg/transition_table" +require "action_dispatch/journey/nfa/transition_table" +require "action_dispatch/journey/gtg/transition_table" module ActionDispatch module Journey # :nodoc: diff --git a/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb b/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb index bfd929357b..fe55861507 100644 --- a/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb +++ b/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "dot" +require "action_dispatch/journey/nfa/dot" module ActionDispatch module Journey # :nodoc: diff --git a/actionpack/lib/action_dispatch/journey/nodes/node.rb b/actionpack/lib/action_dispatch/journey/nodes/node.rb index 0a84f28c1a..08b931a3cd 100644 --- a/actionpack/lib/action_dispatch/journey/nodes/node.rb +++ b/actionpack/lib/action_dispatch/journey/nodes/node.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../visitors" +require "action_dispatch/journey/visitors" module ActionDispatch module Journey # :nodoc: diff --git a/actionpack/lib/action_dispatch/journey/parser.rb b/actionpack/lib/action_dispatch/journey/parser.rb index 6ddfe96098..e002755bcf 100644 --- a/actionpack/lib/action_dispatch/journey/parser.rb +++ b/actionpack/lib/action_dispatch/journey/parser.rb @@ -8,7 +8,7 @@ require 'racc/parser.rb' # :stopdoc: -require_relative "parser_extras" +require "action_dispatch/journey/parser_extras" module ActionDispatch module Journey class Parser < Racc::Parser diff --git a/actionpack/lib/action_dispatch/journey/parser.y b/actionpack/lib/action_dispatch/journey/parser.y index 850c84ea1a..f9b1a7a958 100644 --- a/actionpack/lib/action_dispatch/journey/parser.y +++ b/actionpack/lib/action_dispatch/journey/parser.y @@ -47,4 +47,4 @@ end ---- header # :stopdoc: -require_relative "parser_extras" +require "action_dispatch/journey/parser_extras" diff --git a/actionpack/lib/action_dispatch/journey/parser_extras.rb b/actionpack/lib/action_dispatch/journey/parser_extras.rb index dfbc6c4529..18ec6c9b9b 100644 --- a/actionpack/lib/action_dispatch/journey/parser_extras.rb +++ b/actionpack/lib/action_dispatch/journey/parser_extras.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "scanner" -require_relative "nodes/node" +require "action_dispatch/journey/scanner" +require "action_dispatch/journey/nodes/node" module ActionDispatch # :stopdoc: diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb index 9987a9bfa1..b8fdde5475 100644 --- a/actionpack/lib/action_dispatch/journey/router.rb +++ b/actionpack/lib/action_dispatch/journey/router.rb @@ -1,16 +1,16 @@ # frozen_string_literal: true -require_relative "router/utils" -require_relative "routes" -require_relative "formatter" +require "action_dispatch/journey/router/utils" +require "action_dispatch/journey/routes" +require "action_dispatch/journey/formatter" before = $-w $-w = false -require_relative "parser" +require "action_dispatch/journey/parser" $-w = before -require_relative "route" -require_relative "path/pattern" +require "action_dispatch/journey/route" +require "action_dispatch/journey/path/pattern" module ActionDispatch module Journey # :nodoc: diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb index 3006cd97ce..511306eb0e 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative "../http/request" -require_relative "exception_wrapper" -require_relative "../routing/inspector" +require "action_dispatch/http/request" +require "action_dispatch/middleware/exception_wrapper" +require "action_dispatch/routing/inspector" require "action_view" require "action_view/base" diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index e054fefc9b..5b0be96223 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -3,8 +3,8 @@ require "rack/utils" require "rack/request" require "rack/session/abstract/id" -require_relative "../cookies" -require_relative "../../request/session" +require "action_dispatch/middleware/cookies" +require "action_dispatch/request/session" module ActionDispatch module Session diff --git a/actionpack/lib/action_dispatch/middleware/session/cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb index c84bc8bfad..a6d965a644 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "abstract_store" +require "action_dispatch/middleware/session/abstract_store" module ActionDispatch module Session diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index b0514a96d8..4ea96196d3 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/hash/keys" -require_relative "abstract_store" +require "action_dispatch/middleware/session/abstract_store" require "rack/session/cookie" module ActionDispatch diff --git a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb index f0aec39c9c..914df3a2b1 100644 --- a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "abstract_store" +require "action_dispatch/middleware/session/abstract_store" begin require "rack/session/dalli" rescue LoadError => e diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index d2e739d27f..3c88afd4d3 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../http/request" -require_relative "exception_wrapper" +require "action_dispatch/http/request" +require "action_dispatch/middleware/exception_wrapper" module ActionDispatch # This middleware rescues any exception returned by the application diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 2b43ade081..dea8387c3d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -4,8 +4,8 @@ require "active_support/core_ext/hash/slice" require "active_support/core_ext/enumerable" require "active_support/core_ext/array/extract_options" require "active_support/core_ext/regexp" -require_relative "redirection" -require_relative "endpoint" +require "action_dispatch/routing/redirection" +require "action_dispatch/routing/endpoint" module ActionDispatch module Routing diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index 2e2bc87b57..143a4b3d62 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require_relative "../http/request" +require "action_dispatch/http/request" require "active_support/core_ext/uri" require "active_support/core_ext/array/extract_options" require "rack/utils" require "action_controller/metal/exceptions" -require_relative "endpoint" +require "action_dispatch/routing/endpoint" module ActionDispatch module Routing diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 445e86b13c..18862d819f 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true -require_relative "../journey" +require "action_dispatch/journey" require "active_support/core_ext/object/to_query" require "active_support/core_ext/hash/slice" require "active_support/core_ext/module/redefine_method" require "active_support/core_ext/module/remove_method" require "active_support/core_ext/array/extract_options" require "action_controller/metal/exceptions" -require_relative "../http/request" -require_relative "endpoint" +require "action_dispatch/http/request" +require "action_dispatch/routing/endpoint" module ActionDispatch module Routing diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb index 3f8481ad48..496328bd1d 100644 --- a/actionpack/lib/action_dispatch/system_test_case.rb +++ b/actionpack/lib/action_dispatch/system_test_case.rb @@ -3,11 +3,11 @@ require "capybara/dsl" require "capybara/minitest" require "action_controller" -require_relative "system_testing/driver" -require_relative "system_testing/server" -require_relative "system_testing/test_helpers/screenshot_helper" -require_relative "system_testing/test_helpers/setup_and_teardown" -require_relative "system_testing/test_helpers/undef_methods" +require "action_dispatch/system_testing/driver" +require "action_dispatch/system_testing/server" +require "action_dispatch/system_testing/test_helpers/screenshot_helper" +require "action_dispatch/system_testing/test_helpers/setup_and_teardown" +require "action_dispatch/system_testing/test_helpers/undef_methods" module ActionDispatch # = System Testing diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 8caa71199e..7171b6942c 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -7,7 +7,7 @@ require "active_support/core_ext/object/try" require "rack/test" require "minitest" -require_relative "request_encoder" +require "action_dispatch/testing/request_encoder" module ActionDispatch module Integration #:nodoc: diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb index 3b63706aaa..8ac50c730d 100644 --- a/actionpack/lib/action_dispatch/testing/test_process.rb +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative "../middleware/cookies" -require_relative "../middleware/flash" +require "action_dispatch/middleware/cookies" +require "action_dispatch/middleware/flash" module ActionDispatch module TestProcess diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb index b23ea7479c..1e6b21f235 100644 --- a/actionpack/lib/action_dispatch/testing/test_response.rb +++ b/actionpack/lib/action_dispatch/testing/test_response.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "request_encoder" +require "action_dispatch/testing/request_encoder" module ActionDispatch # Integration test methods such as ActionDispatch::Integration::Session#get diff --git a/actionpack/lib/action_pack.rb b/actionpack/lib/action_pack.rb index fe2fc7c474..95fdd3affb 100644 --- a/actionpack/lib/action_pack.rb +++ b/actionpack/lib/action_pack.rb @@ -23,4 +23,4 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -require_relative "action_pack/version" +require "action_pack/version" -- cgit v1.2.3 From 8ae2fdd267d59d394be3e6724b357b11a8046784 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:21:02 +0900 Subject: [Action View] require_relative => require This basically reverts c4d1a4efeec6f0b5b58222993aa0bec85a19b6a8 --- actionview/lib/action_view.rb | 2 +- actionview/lib/action_view/base.rb | 10 +++++----- actionview/lib/action_view/dependency_tracker.rb | 2 +- actionview/lib/action_view/digestor.rb | 2 +- actionview/lib/action_view/helpers/asset_tag_helper.rb | 4 ++-- actionview/lib/action_view/helpers/date_helper.rb | 2 +- actionview/lib/action_view/helpers/form_helper.rb | 12 ++++++------ actionview/lib/action_view/helpers/form_options_helper.rb | 2 +- actionview/lib/action_view/helpers/form_tag_helper.rb | 2 +- actionview/lib/action_view/helpers/javascript_helper.rb | 2 +- actionview/lib/action_view/helpers/tags/check_box.rb | 2 +- .../lib/action_view/helpers/tags/collection_check_boxes.rb | 2 +- .../lib/action_view/helpers/tags/collection_radio_buttons.rb | 2 +- actionview/lib/action_view/helpers/tags/radio_button.rb | 2 +- actionview/lib/action_view/helpers/tags/text_area.rb | 2 +- actionview/lib/action_view/helpers/tags/text_field.rb | 2 +- actionview/lib/action_view/helpers/translation_helper.rb | 2 +- actionview/lib/action_view/helpers/url_helper.rb | 2 +- actionview/lib/action_view/layouts.rb | 2 +- actionview/lib/action_view/lookup_context.rb | 2 +- actionview/lib/action_view/record_identifier.rb | 2 +- actionview/lib/action_view/renderer/partial_renderer.rb | 2 +- actionview/lib/action_view/rendering.rb | 2 +- actionview/lib/action_view/template/resolver.rb | 2 +- actionview/lib/action_view/testing/resolvers.rb | 2 +- 25 files changed, 35 insertions(+), 35 deletions(-) diff --git a/actionview/lib/action_view.rb b/actionview/lib/action_view.rb index 3c8a8488a5..2069ea042a 100644 --- a/actionview/lib/action_view.rb +++ b/actionview/lib/action_view.rb @@ -25,7 +25,7 @@ require "active_support" require "active_support/rails" -require_relative "action_view/version" +require "action_view/version" module ActionView extend ActiveSupport::Autoload diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index 637c8e7708..d41fe2a608 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -3,11 +3,11 @@ require "active_support/core_ext/module/attr_internal" require "active_support/core_ext/module/attribute_accessors" require "active_support/ordered_options" -require_relative "log_subscriber" -require_relative "helpers" -require_relative "context" -require_relative "template" -require_relative "lookup_context" +require "action_view/log_subscriber" +require "action_view/helpers" +require "action_view/context" +require "action_view/template" +require "action_view/lookup_context" module ActionView #:nodoc: # = Action View Base diff --git a/actionview/lib/action_view/dependency_tracker.rb b/actionview/lib/action_view/dependency_tracker.rb index 02bd0545c2..182f6e2eef 100644 --- a/actionview/lib/action_view/dependency_tracker.rb +++ b/actionview/lib/action_view/dependency_tracker.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "concurrent/map" -require_relative "path_set" +require "action_view/path_set" module ActionView class DependencyTracker # :nodoc: diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb index e404ebb6b6..dfd62bdcfd 100644 --- a/actionview/lib/action_view/digestor.rb +++ b/actionview/lib/action_view/digestor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "concurrent/map" -require_relative "dependency_tracker" +require "action_view/dependency_tracker" require "monitor" module ActionView diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index 10b366b030..e362f13798 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -2,8 +2,8 @@ require "active_support/core_ext/array/extract_options" require "active_support/core_ext/hash/keys" -require_relative "asset_url_helper" -require_relative "tag_helper" +require "action_view/helpers/asset_url_helper" +require "action_view/helpers/tag_helper" module ActionView # = Action View Asset Tag Helpers diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 642bd0fec6..09040ccbc4 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "date" -require_relative "tag_helper" +require "action_view/helpers/tag_helper" require "active_support/core_ext/array/extract_options" require "active_support/core_ext/date/conversions" require "active_support/core_ext/hash/slice" diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 6702c65ccb..6d2ace8cf8 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true require "cgi" -require_relative "date_helper" -require_relative "tag_helper" -require_relative "form_tag_helper" -require_relative "active_model_helper" -require_relative "../model_naming" -require_relative "../record_identifier" +require "action_view/helpers/date_helper" +require "action_view/helpers/tag_helper" +require "action_view/helpers/form_tag_helper" +require "action_view/helpers/active_model_helper" +require "action_view/model_naming" +require "action_view/record_identifier" require "active_support/core_ext/module/attribute_accessors" require "active_support/core_ext/hash/slice" require "active_support/core_ext/string/output_safety" diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb index 38b185f126..02a44477c1 100644 --- a/actionview/lib/action_view/helpers/form_options_helper.rb +++ b/actionview/lib/action_view/helpers/form_options_helper.rb @@ -2,7 +2,7 @@ require "cgi" require "erb" -require_relative "form_helper" +require "action_view/helpers/form_helper" require "active_support/core_ext/string/output_safety" require "active_support/core_ext/array/extract_options" require "active_support/core_ext/array/wrap" diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 8a08e49e2f..80d53c2c6e 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "cgi" -require_relative "tag_helper" +require "action_view/helpers/tag_helper" require "active_support/core_ext/string/output_safety" require "active_support/core_ext/module/attribute_accessors" diff --git a/actionview/lib/action_view/helpers/javascript_helper.rb b/actionview/lib/action_view/helpers/javascript_helper.rb index 11eefe0ee0..dd2cd57ac3 100644 --- a/actionview/lib/action_view/helpers/javascript_helper.rb +++ b/actionview/lib/action_view/helpers/javascript_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "tag_helper" +require "action_view/helpers/tag_helper" module ActionView module Helpers #:nodoc: diff --git a/actionview/lib/action_view/helpers/tags/check_box.rb b/actionview/lib/action_view/helpers/tags/check_box.rb index 6b34dfef90..4327e07cae 100644 --- a/actionview/lib/action_view/helpers/tags/check_box.rb +++ b/actionview/lib/action_view/helpers/tags/check_box.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "checkable" +require "action_view/helpers/tags/checkable" module ActionView module Helpers 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 91c1135d20..455442178e 100644 --- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb +++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "collection_helpers" +require "action_view/helpers/tags/collection_helpers" module ActionView module Helpers diff --git a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb index 0b0482f74e..16d37134e5 100644 --- a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb +++ b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "collection_helpers" +require "action_view/helpers/tags/collection_helpers" module ActionView module Helpers diff --git a/actionview/lib/action_view/helpers/tags/radio_button.rb b/actionview/lib/action_view/helpers/tags/radio_button.rb index 3cfdcbea3f..621db2b1b5 100644 --- a/actionview/lib/action_view/helpers/tags/radio_button.rb +++ b/actionview/lib/action_view/helpers/tags/radio_button.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "checkable" +require "action_view/helpers/tags/checkable" module ActionView module Helpers diff --git a/actionview/lib/action_view/helpers/tags/text_area.rb b/actionview/lib/action_view/helpers/tags/text_area.rb index 9c162b59f5..4519082ff6 100644 --- a/actionview/lib/action_view/helpers/tags/text_area.rb +++ b/actionview/lib/action_view/helpers/tags/text_area.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "placeholderable" +require "action_view/helpers/tags/placeholderable" module ActionView module Helpers diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb index 3553942048..d92967e212 100644 --- a/actionview/lib/action_view/helpers/tags/text_field.rb +++ b/actionview/lib/action_view/helpers/tags/text_field.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "placeholderable" +require "action_view/helpers/tags/placeholderable" module ActionView module Helpers diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index e663892592..1860bc4732 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "tag_helper" +require "action_view/helpers/tag_helper" require "active_support/core_ext/string/access" require "i18n/exceptions" diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 2d5aac6dc7..efda549c0d 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "javascript_helper" +require "action_view/helpers/javascript_helper" require "active_support/core_ext/array/access" require "active_support/core_ext/hash/keys" require "active_support/core_ext/string/output_safety" diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb index b11ef6e133..3e6d352c15 100644 --- a/actionview/lib/action_view/layouts.rb +++ b/actionview/lib/action_view/layouts.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "rendering" +require "action_view/rendering" require "active_support/core_ext/module/redefine_method" module ActionView diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index acc0f57f1d..0e56eca35c 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -3,7 +3,7 @@ require "concurrent/map" require "active_support/core_ext/module/remove_method" require "active_support/core_ext/module/attribute_accessors" -require_relative "template/resolver" +require "action_view/template/resolver" module ActionView # = Action View Lookup Context diff --git a/actionview/lib/action_view/record_identifier.rb b/actionview/lib/action_view/record_identifier.rb index b34a793c89..1310a1ce0a 100644 --- a/actionview/lib/action_view/record_identifier.rb +++ b/actionview/lib/action_view/record_identifier.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/module" -require_relative "model_naming" +require "action_view/model_naming" module ActionView # RecordIdentifier encapsulates methods used by various ActionView helpers diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index beb0a18b65..5b40af4f2f 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "concurrent/map" -require_relative "partial_renderer/collection_caching" +require "action_view/renderer/partial_renderer/collection_caching" module ActionView class PartialIteration diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index 62b5047f56..4e5fdfbb2d 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "view_paths" +require "action_view/view_paths" module ActionView # This is a class to fix I18n global state. Whenever you provide I18n.locale during a request, diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index a58d375293..5a86f10973 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -3,7 +3,7 @@ require "pathname" require "active_support/core_ext/class" require "active_support/core_ext/module/attribute_accessors" -require_relative "../template" +require "action_view/template" require "thread" require "concurrent/map" diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb index 92fdb24a5e..68186c3bf8 100644 --- a/actionview/lib/action_view/testing/resolvers.rb +++ b/actionview/lib/action_view/testing/resolvers.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "../template/resolver" +require "action_view/template/resolver" module ActionView #:nodoc: # Use FixtureResolver in your tests to simulate the presence of files on the -- cgit v1.2.3 From a53a52d8ca6741b9884bcbdef98cd80b0b9c8bd5 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:22:17 +0900 Subject: [Action Mailer] require_relative => require This basically reverts cd9cc721ab54e2b0c7875cacf2113f03908a8bb7 --- actionmailer/lib/action_mailer.rb | 2 +- actionmailer/lib/action_mailer/base.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index a170eb7917..ade3fe39a2 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -24,7 +24,7 @@ #++ require "abstract_controller" -require_relative "action_mailer/version" +require "action_mailer/version" # Common Active Support usage in Action Mailer require "active_support" diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 1c1e1a9a3b..eb8ae59533 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true require "mail" -require_relative "collector" +require "action_mailer/collector" require "active_support/core_ext/string/inflections" require "active_support/core_ext/hash/except" require "active_support/core_ext/module/anonymous" -require_relative "log_subscriber" -require_relative "rescuable" +require "action_mailer/log_subscriber" +require "action_mailer/rescuable" module ActionMailer # Action Mailer allows you to send email from your application using a mailer model and views. -- cgit v1.2.3 From feb6ecd3130f852b86226e0a592f1f40365d89b4 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:23:16 +0900 Subject: [Active Job] require_relative => require This basically reverts fef234f1f0a238c2277459652861144ae89501ff --- activejob/lib/active_job.rb | 2 +- activejob/lib/active_job/base.rb | 20 ++++++++++---------- activejob/lib/active_job/enqueuing.rb | 2 +- activejob/lib/active_job/execution.rb | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb index 56dab66544..1f5bd7b3e9 100644 --- a/activejob/lib/active_job.rb +++ b/activejob/lib/active_job.rb @@ -25,7 +25,7 @@ require "active_support" require "active_support/rails" -require_relative "active_job/version" +require "active_job/version" require "global_id" module ActiveJob diff --git a/activejob/lib/active_job/base.rb b/activejob/lib/active_job/base.rb index 6af41260db..ae112abb2c 100644 --- a/activejob/lib/active_job/base.rb +++ b/activejob/lib/active_job/base.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require_relative "core" -require_relative "queue_adapter" -require_relative "queue_name" -require_relative "queue_priority" -require_relative "enqueuing" -require_relative "execution" -require_relative "callbacks" -require_relative "exceptions" -require_relative "logging" -require_relative "translation" +require "active_job/core" +require "active_job/queue_adapter" +require "active_job/queue_name" +require "active_job/queue_priority" +require "active_job/enqueuing" +require "active_job/execution" +require "active_job/callbacks" +require "active_job/exceptions" +require "active_job/logging" +require "active_job/translation" module ActiveJob #:nodoc: # = Active Job diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb index c0d016853c..53cb98fc71 100644 --- a/activejob/lib/active_job/enqueuing.rb +++ b/activejob/lib/active_job/enqueuing.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "arguments" +require "active_job/arguments" module ActiveJob # Provides behavior for enqueuing jobs. diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb index 85e050b489..d75be376ec 100644 --- a/activejob/lib/active_job/execution.rb +++ b/activejob/lib/active_job/execution.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/rescuable" -require_relative "arguments" +require "active_job/arguments" module ActiveJob module Execution -- cgit v1.2.3 From 40a8db632958641a25f1a21d1e4ad3315ace9351 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:24:35 +0900 Subject: [Action Cable] require_relative => require This basically reverts f851e1f705f26d8f92f0fc1b265b20bc389d23cb --- actioncable/lib/action_cable.rb | 2 +- actioncable/lib/action_cable/engine.rb | 2 +- actioncable/lib/action_cable/subscription_adapter/async.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actioncable/lib/action_cable.rb b/actioncable/lib/action_cable.rb index bd828b2d0f..001a043409 100644 --- a/actioncable/lib/action_cable.rb +++ b/actioncable/lib/action_cable.rb @@ -25,7 +25,7 @@ require "active_support" require "active_support/rails" -require_relative "action_cable/version" +require "action_cable/version" module ActionCable extend ActiveSupport::Autoload diff --git a/actioncable/lib/action_cable/engine.rb b/actioncable/lib/action_cable/engine.rb index c961f47342..53cbb597cd 100644 --- a/actioncable/lib/action_cable/engine.rb +++ b/actioncable/lib/action_cable/engine.rb @@ -2,7 +2,7 @@ require "rails" require "action_cable" -require_relative "helpers/action_cable_helper" +require "action_cable/helpers/action_cable_helper" require "active_support/core_ext/hash/indifferent_access" module ActionCable diff --git a/actioncable/lib/action_cable/subscription_adapter/async.rb b/actioncable/lib/action_cable/subscription_adapter/async.rb index 96c18c4a2f..c9930299c7 100644 --- a/actioncable/lib/action_cable/subscription_adapter/async.rb +++ b/actioncable/lib/action_cable/subscription_adapter/async.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "inline" +require "action_cable/subscription_adapter/inline" module ActionCable module SubscriptionAdapter -- cgit v1.2.3 From 20c91119903f70eb19aed33fe78417789dbf070f Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 21 Oct 2017 22:27:49 +0900 Subject: [Active Storage] require_relative => require --- activestorage/lib/active_storage.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb index 44d9c25504..9ac1e7ab54 100644 --- a/activestorage/lib/active_storage.rb +++ b/activestorage/lib/active_storage.rb @@ -26,7 +26,7 @@ require "active_record" require "active_support" require "active_support/rails" -require_relative "active_storage/version" +require "active_storage/version" module ActiveStorage extend ActiveSupport::Autoload -- cgit v1.2.3 From 20df3f778649d50498a5c04b98abd2379f56b31c Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sun, 22 Oct 2017 01:42:02 +0900 Subject: Avoid slicing from Thor's original HWIA Because `options` here is not AS::HWIA but an instance of Thor::CoreExt::HWIA that looks very similar to ours but behaves slightly different, we need to keep this object be an instance of Thor::CoreExt::HWIA. Since Ruby 2.5 has Hash#slice that returns a new Hash instance now, we need to avoid calling `slice` on this tricky object. --- railties/lib/rails/generators/rails/plugin/plugin_generator.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index d0dfb04162..331e73d1f0 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "active_support/core_ext/hash/slice" require "rails/generators/rails/app/app_generator" require "date" @@ -93,7 +92,7 @@ task default: :test ] def generate_test_dummy(force = false) - opts = (options || {}).slice(*PASSTHROUGH_OPTIONS) + opts = (options.dup || {}).keep_if {|k, | PASSTHROUGH_OPTIONS.map(&:to_s).include?(k) } opts[:force] = force opts[:skip_bundle] = true opts[:skip_listen] = true -- cgit v1.2.3 From 75597f064a21ba6682678edf10ae18c33653662d Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sat, 21 Oct 2017 18:01:57 +0300 Subject: Add changelog entry about new `allow_other_host` option for `redirect_back` method [ci skip] Related to #30850 --- actionpack/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 9a001cef9b..e5c814cc79 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,10 @@ +* Add `:allow_other_host` option to `redirect_back` method. + When `allow_other_host` is set to `false`, the `redirect_back` + will not allow a redirecting from a different host. + `allow_other_host` is `true` by default. + + *Tim Masliuchenko* + * Add headless chrome support to System Tests. *Yuji Yaginuma* -- cgit v1.2.3 From 605484079d297d1ba6835628465be81f03c052ee Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Sun, 22 Oct 2017 13:16:59 -0400 Subject: Extract metadata from images and videos --- .../app/jobs/active_storage/analyze_job.rb | 8 +++ activestorage/app/jobs/active_storage/purge_job.rb | 2 +- .../app/models/active_storage/attachment.rb | 7 ++ activestorage/app/models/active_storage/blob.rb | 57 ++++++++++++++- activestorage/lib/active_storage.rb | 3 + activestorage/lib/active_storage/analyzer.rb | 33 +++++++++ .../lib/active_storage/analyzer/image_analyzer.rb | 36 ++++++++++ .../lib/active_storage/analyzer/null_analyzer.rb | 13 ++++ .../lib/active_storage/analyzer/video_analyzer.rb | 79 +++++++++++++++++++++ activestorage/lib/active_storage/attached/one.rb | 6 +- activestorage/lib/active_storage/downloading.rb | 21 ++++++ activestorage/lib/active_storage/engine.rb | 14 +++- activestorage/lib/active_storage/log_subscriber.rb | 2 +- activestorage/lib/active_storage/previewer.rb | 27 ++----- .../lib/active_storage/previewer/pdf_previewer.rb | 2 +- .../active_storage/previewer/video_previewer.rb | 10 ++- activestorage/lib/active_storage/service.rb | 2 - activestorage/test/analyzer/image_analyzer_test.rb | 16 +++++ activestorage/test/analyzer/video_analyzer_test.rb | 35 +++++++++ .../test/fixtures/files/rotated_video.mp4 | Bin 0 -> 275090 bytes .../fixtures/files/video_without_video_stream.mp4 | Bin 0 -> 16252 bytes activestorage/test/models/attachments_test.rb | 59 ++++++++++++++- activestorage/test/previewer/pdf_previewer_test.rb | 3 + .../test/previewer/video_previewer_test.rb | 3 + activestorage/test/test_helper.rb | 6 +- 25 files changed, 407 insertions(+), 37 deletions(-) create mode 100644 activestorage/app/jobs/active_storage/analyze_job.rb create mode 100644 activestorage/lib/active_storage/analyzer.rb create mode 100644 activestorage/lib/active_storage/analyzer/image_analyzer.rb create mode 100644 activestorage/lib/active_storage/analyzer/null_analyzer.rb create mode 100644 activestorage/lib/active_storage/analyzer/video_analyzer.rb create mode 100644 activestorage/lib/active_storage/downloading.rb create mode 100644 activestorage/test/analyzer/image_analyzer_test.rb create mode 100644 activestorage/test/analyzer/video_analyzer_test.rb create mode 100644 activestorage/test/fixtures/files/rotated_video.mp4 create mode 100644 activestorage/test/fixtures/files/video_without_video_stream.mp4 diff --git a/activestorage/app/jobs/active_storage/analyze_job.rb b/activestorage/app/jobs/active_storage/analyze_job.rb new file mode 100644 index 0000000000..a11a73d030 --- /dev/null +++ b/activestorage/app/jobs/active_storage/analyze_job.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Provides asynchronous analysis of ActiveStorage::Blob records via ActiveStorage::Blob#analyze_later. +class ActiveStorage::AnalyzeJob < ActiveJob::Base + def perform(blob) + blob.analyze + end +end diff --git a/activestorage/app/jobs/active_storage/purge_job.rb b/activestorage/app/jobs/active_storage/purge_job.rb index 990ab27c9f..188840f702 100644 --- a/activestorage/app/jobs/active_storage/purge_job.rb +++ b/activestorage/app/jobs/active_storage/purge_job.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Provides delayed purging of ActiveStorage::Blob records via ActiveStorage::Blob#purge_later. +# Provides asynchronous purging of ActiveStorage::Blob records via ActiveStorage::Blob#purge_later. class ActiveStorage::PurgeJob < ActiveJob::Base # FIXME: Limit this to a custom ActiveStorage error retry_on StandardError diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb index 29226e8ee9..9f61a5dbf3 100644 --- a/activestorage/app/models/active_storage/attachment.rb +++ b/activestorage/app/models/active_storage/attachment.rb @@ -14,6 +14,8 @@ class ActiveStorage::Attachment < ActiveRecord::Base delegate_missing_to :blob + after_create_commit :analyze_blob_later + # Synchronously purges the blob (deletes it from the configured service) and destroys the attachment. def purge blob.purge @@ -25,4 +27,9 @@ class ActiveStorage::Attachment < ActiveRecord::Base blob.purge_later destroy end + + private + def analyze_blob_later + blob.analyze_later unless blob.analyzed? + end end diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 84b8f3827b..99823e14c6 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "active_storage/analyzer/null_analyzer" + # A blob is a record that contains the metadata about a file and a key for where that file resides on the service. # Blobs can be created in two ways: # @@ -20,7 +22,7 @@ class ActiveStorage::Blob < ActiveRecord::Base self.table_name = "active_storage_blobs" has_secure_token :key - store :metadata, coder: JSON + store :metadata, accessors: [ :analyzed ], coder: JSON class_attribute :service @@ -224,6 +226,46 @@ class ActiveStorage::Blob < ActiveRecord::Base end + # Extracts and stores metadata from the file associated with this blob using a relevant analyzer. Active Storage comes + # with built-in analyzers for images and videos. See ActiveStorage::Analyzer::ImageAnalyzer and + # ActiveStorage::Analyzer::VideoAnalyzer for information about the specific attributes they extract and the third-party + # libraries they require. + # + # To choose the analyzer for a blob, Active Storage calls +accept?+ on each registered analyzer in order. It uses the + # first analyzer for which +accept?+ returns true when given the blob. If no registered analyzer accepts the blob, no + # metadata is extracted from it. + # + # In a Rails application, add or remove analyzers by manipulating +Rails.application.config.active_storage.analyzers+ + # in an initializer: + # + # # Add a custom analyzer for Microsoft Office documents: + # Rails.application.config.active_storage.analyzers.append DOCXAnalyzer + # + # # Remove the built-in video analyzer: + # Rails.application.config.active_storage.analyzers.delete ActiveStorage::Analyzer::VideoAnalyzer + # + # Outside of a Rails application, manipulate +ActiveStorage.analyzers+ instead. + # + # You won't ordinarily need to call this method from a Rails application. New blobs are automatically and asynchronously + # analyzed via #analyze_later when they're attached for the first time. + def analyze + update! metadata: extract_metadata_via_analyzer + end + + # Enqueues an ActiveStorage::AnalyzeJob which calls #analyze. + # + # This method is automatically called for a blob when it's attached for the first time. You can call it to analyze a blob + # again (e.g. if you add a new analyzer or modify an existing one). + def analyze_later + ActiveStorage::AnalyzeJob.perform_later(self) + end + + # Returns true if the blob has been analyzed. + def analyzed? + analyzed + end + + # Deletes the file on the service that's associated with this blob. This should only be done if the blob is going to be # deleted as well or you will essentially have a dead reference. It's recommended to use the +#purge+ and +#purge_later+ # methods in most circumstances. @@ -255,4 +297,17 @@ class ActiveStorage::Blob < ActiveRecord::Base io.rewind end.base64digest end + + + def extract_metadata_via_analyzer + analyzer.metadata.merge(analyzed: true) + end + + def analyzer + analyzer_class.new(self) + end + + def analyzer_class + ActiveStorage.analyzers.detect { |klass| klass.accept?(self) } || ActiveStorage::Analyzer::NullAnalyzer + end end diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb index 9ac1e7ab54..cfdb2a8acd 100644 --- a/activestorage/lib/active_storage.rb +++ b/activestorage/lib/active_storage.rb @@ -34,7 +34,10 @@ module ActiveStorage autoload :Attached autoload :Service autoload :Previewer + autoload :Analyzer + mattr_accessor :logger mattr_accessor :verifier mattr_accessor :previewers, default: [] + mattr_accessor :analyzers, default: [] end diff --git a/activestorage/lib/active_storage/analyzer.rb b/activestorage/lib/active_storage/analyzer.rb new file mode 100644 index 0000000000..837785a12b --- /dev/null +++ b/activestorage/lib/active_storage/analyzer.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "active_storage/downloading" + +module ActiveStorage + # This is an abstract base class for analyzers, which extract metadata from blobs. See + # ActiveStorage::Analyzer::ImageAnalyzer for an example of a concrete subclass. + class Analyzer + include Downloading + + attr_reader :blob + + # Implement this method in a concrete subclass. Have it return true when given a blob from which + # the analyzer can extract metadata. + def self.accept?(blob) + false + end + + def initialize(blob) + @blob = blob + end + + # Override this method in a concrete subclass. Have it return a Hash of metadata. + def metadata + raise NotImplementedError + end + + private + def logger + ActiveStorage.logger + end + end +end diff --git a/activestorage/lib/active_storage/analyzer/image_analyzer.rb b/activestorage/lib/active_storage/analyzer/image_analyzer.rb new file mode 100644 index 0000000000..b25d2092b9 --- /dev/null +++ b/activestorage/lib/active_storage/analyzer/image_analyzer.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module ActiveStorage + # Extracts width and height in pixels from an image blob. + # + # Example: + # + # ActiveStorage::Analyzer::ImageAnalyzer.new(blob).metadata + # # => { width: 4104, height: 2736 } + # + # This analyzer relies on the third-party [MiniMagick]{https://github.com/minimagick/minimagick} gem. MiniMagick requires + # the [ImageMagick]{http://www.imagemagick.org} system library. These libraries are not provided by Rails; you must + # install them yourself to use this analyzer. + class Analyzer::ImageAnalyzer < Analyzer + def self.accept?(blob) + blob.image? + end + + def metadata + read_image do |image| + { width: image.width, height: image.height } + end + rescue LoadError + logger.info "Skipping image analysis because the mini_magick gem isn't installed" + {} + end + + private + def read_image + download_blob_to_tempfile do |file| + require "mini_magick" + yield MiniMagick::Image.new(file.path) + end + end + end +end diff --git a/activestorage/lib/active_storage/analyzer/null_analyzer.rb b/activestorage/lib/active_storage/analyzer/null_analyzer.rb new file mode 100644 index 0000000000..8ff7ce48e5 --- /dev/null +++ b/activestorage/lib/active_storage/analyzer/null_analyzer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module ActiveStorage + class Analyzer::NullAnalyzer < Analyzer # :nodoc: + def self.accept?(blob) + true + end + + def metadata + {} + end + end +end diff --git a/activestorage/lib/active_storage/analyzer/video_analyzer.rb b/activestorage/lib/active_storage/analyzer/video_analyzer.rb new file mode 100644 index 0000000000..408b5e58e9 --- /dev/null +++ b/activestorage/lib/active_storage/analyzer/video_analyzer.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require "active_support/core_ext/hash/compact" + +module ActiveStorage + # Extracts the following from a video blob: + # + # * Width (pixels) + # * Height (pixels) + # * Duration (seconds) + # * Angle (degrees) + # * Aspect ratio + # + # Example: + # + # ActiveStorage::VideoAnalyzer.new(blob).metadata + # # => { width: 640, height: 480, duration: 5.0, angle: 0, aspect_ratio: [4, 3] } + # + # This analyzer requires the {ffmpeg}[https://www.ffmpeg.org] system library, which is not provided by Rails. You must + # install ffmpeg yourself to use this analyzer. + class Analyzer::VideoAnalyzer < Analyzer + def self.accept?(blob) + blob.video? + end + + def metadata + { width: width, height: height, duration: duration, angle: angle, aspect_ratio: aspect_ratio }.compact + end + + private + def width + Integer(video_stream["width"]) if video_stream["width"] + end + + def height + Integer(video_stream["height"]) if video_stream["height"] + end + + def duration + Float(video_stream["duration"]) if video_stream["duration"] + end + + def angle + Integer(tags["rotate"]) if tags["rotate"] + end + + def aspect_ratio + if descriptor = video_stream["display_aspect_ratio"] + descriptor.split(":", 2).collect(&:to_i) + end + end + + + def tags + @tags ||= video_stream["tags"] || {} + end + + def video_stream + @video_stream ||= streams.detect { |stream| stream["codec_type"] == "video" } || {} + end + + def streams + probe["streams"] || [] + end + + def probe + download_blob_to_tempfile { |file| probe_from(file) } + end + + def probe_from(file) + IO.popen([ "ffprobe", "-print_format", "json", "-show_streams", "-v", "error", file.path ]) do |output| + JSON.parse(output.read) + end + rescue Errno::ENOENT + logger.info "Skipping video analysis because ffmpeg isn't installed" + {} + end + end +end diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb index c66be08f58..dc19512484 100644 --- a/activestorage/lib/active_storage/attached/one.rb +++ b/activestorage/lib/active_storage/attached/one.rb @@ -59,12 +59,16 @@ module ActiveStorage def replace(attachable) blob.tap do transaction do - destroy + destroy_attachment write_attachment create_attachment_from(attachable) end end.purge_later end + def destroy_attachment + attachment.destroy + end + def create_attachment_from(attachable) ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable)) end diff --git a/activestorage/lib/active_storage/downloading.rb b/activestorage/lib/active_storage/downloading.rb new file mode 100644 index 0000000000..bcf42f610e --- /dev/null +++ b/activestorage/lib/active_storage/downloading.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ActiveStorage + module Downloading + private + # Opens a new tempfile and copies blob data into it. Yields the tempfile. + def download_blob_to_tempfile # :doc: + Tempfile.open("ActiveStorage") do |file| + download_blob_to file + yield file + end + end + + # Efficiently download blob data into the given file. + def download_blob_to(file) # :doc: + file.binmode + blob.download { |chunk| file.write(chunk) } + file.rewind + end + end +end diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb index 335eae8dd8..a01a14cd83 100644 --- a/activestorage/lib/active_storage/engine.rb +++ b/activestorage/lib/active_storage/engine.rb @@ -6,20 +6,22 @@ require "active_storage" require "active_storage/previewer/pdf_previewer" require "active_storage/previewer/video_previewer" +require "active_storage/analyzer/image_analyzer" +require "active_storage/analyzer/video_analyzer" + module ActiveStorage class Engine < Rails::Engine # :nodoc: isolate_namespace ActiveStorage config.active_storage = ActiveSupport::OrderedOptions.new config.active_storage.previewers = [ ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ] + config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ] config.eager_load_namespaces << ActiveStorage initializer "active_storage.logger" do - require "active_storage/service" - config.after_initialize do |app| - ActiveStorage::Service.logger = app.config.active_storage.logger || Rails.logger + ActiveStorage.logger = app.config.active_storage.logger || Rails.logger end end @@ -69,5 +71,11 @@ module ActiveStorage ActiveStorage.previewers = app.config.active_storage.previewers || [] end end + + initializer "active_storage.analyzers" do + config.after_initialize do |app| + ActiveStorage.analyzers = app.config.active_storage.analyzers || [] + end + end end end diff --git a/activestorage/lib/active_storage/log_subscriber.rb b/activestorage/lib/active_storage/log_subscriber.rb index 0d00a75c0e..5cbf4bd1a5 100644 --- a/activestorage/lib/active_storage/log_subscriber.rb +++ b/activestorage/lib/active_storage/log_subscriber.rb @@ -27,7 +27,7 @@ module ActiveStorage end def logger - ActiveStorage::Service.logger + ActiveStorage.logger end private diff --git a/activestorage/lib/active_storage/previewer.rb b/activestorage/lib/active_storage/previewer.rb index 930b376067..460f6d5678 100644 --- a/activestorage/lib/active_storage/previewer.rb +++ b/activestorage/lib/active_storage/previewer.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true +require "active_storage/downloading" + module ActiveStorage # This is an abstract base class for previewers, which generate images from blobs. See # ActiveStorage::Previewer::PDFPreviewer and ActiveStorage::Previewer::VideoPreviewer for examples of # concrete subclasses. class Previewer + include Downloading + attr_reader :blob # Implement this method in a concrete subclass. Have it return true when given a blob from which @@ -24,37 +28,20 @@ module ActiveStorage end private - # Downloads the blob to a new tempfile. Yields the tempfile. - # - # Use this method to get a tempfile that you can provide to a drawing command. - def open # :doc: - Tempfile.open("input") do |file| - download_blob_to file - yield file - end - end - - def download_blob_to(file) - file.binmode - blob.download { |chunk| file.write(chunk) } - file.rewind - end - - # Executes a system command, capturing its binary output in a tempfile. Yields the tempfile. # - # Use this method to shell out to system libraries (e.g. mupdf or ffmpeg) for preview image + # Use this method to shell out to a system library (e.g. mupdf or ffmpeg) for preview image # generation. The resulting tempfile can be used as the +:io+ value in an attachable Hash: # # def preview - # open do |input| + # download_blob_to_tempfile do |input| # draw "my-drawing-command", input.path, "--format", "png", "-" do |output| # yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" # end # end # end def draw(*argv) # :doc: - Tempfile.open("output") do |file| + Tempfile.open("ActiveStorage") do |file| capture(*argv, to: file) yield file end diff --git a/activestorage/lib/active_storage/previewer/pdf_previewer.rb b/activestorage/lib/active_storage/previewer/pdf_previewer.rb index 31a2a8f120..a2f05c74a6 100644 --- a/activestorage/lib/active_storage/previewer/pdf_previewer.rb +++ b/activestorage/lib/active_storage/previewer/pdf_previewer.rb @@ -7,7 +7,7 @@ module ActiveStorage end def preview - open do |input| + download_blob_to_tempfile do |input| draw "mutool", "draw", "-F", "png", "-o", "-", input.path, "1" do |output| yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" end diff --git a/activestorage/lib/active_storage/previewer/video_previewer.rb b/activestorage/lib/active_storage/previewer/video_previewer.rb index 840d87f100..49f128d142 100644 --- a/activestorage/lib/active_storage/previewer/video_previewer.rb +++ b/activestorage/lib/active_storage/previewer/video_previewer.rb @@ -7,11 +7,17 @@ module ActiveStorage end def preview - open do |input| - draw "ffmpeg", "-i", input.path, "-y", "-vcodec", "png", "-vf", "thumbnail", "-vframes", "1", "-f", "image2", "-" do |output| + download_blob_to_tempfile do |input| + draw_relevant_frame_from input do |output| yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" end end end + + private + def draw_relevant_frame_from(file, &block) + draw "ffmpeg", "-i", file.path, "-y", "-vcodec", "png", + "-vf", "thumbnail", "-vframes", "1", "-f", "image2", "-", &block + end end end diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb index 1f012da1e7..aa150e4d8a 100644 --- a/activestorage/lib/active_storage/service.rb +++ b/activestorage/lib/active_storage/service.rb @@ -41,8 +41,6 @@ module ActiveStorage extend ActiveSupport::Autoload autoload :Configurator - class_attribute :logger - class_attribute :url_expires_in, default: 5.minutes class << self diff --git a/activestorage/test/analyzer/image_analyzer_test.rb b/activestorage/test/analyzer/image_analyzer_test.rb new file mode 100644 index 0000000000..9087072215 --- /dev/null +++ b/activestorage/test/analyzer/image_analyzer_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +require "active_storage/analyzer/image_analyzer" + +class ActiveStorage::Analyzer::ImageAnalyzerTest < ActiveSupport::TestCase + test "analyzing an image" do + blob = create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg") + metadata = blob.tap(&:analyze).metadata + + assert_equal 4104, metadata[:width] + assert_equal 2736, metadata[:height] + end +end diff --git a/activestorage/test/analyzer/video_analyzer_test.rb b/activestorage/test/analyzer/video_analyzer_test.rb new file mode 100644 index 0000000000..4a3c4a8bfc --- /dev/null +++ b/activestorage/test/analyzer/video_analyzer_test.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +require "active_storage/analyzer/video_analyzer" + +class ActiveStorage::Analyzer::VideoAnalyzerTest < ActiveSupport::TestCase + test "analyzing a video" do + blob = create_file_blob(filename: "video.mp4", content_type: "video/mp4") + metadata = blob.tap(&:analyze).metadata + + assert_equal 640, metadata[:width] + assert_equal 480, metadata[:height] + assert_equal [4, 3], metadata[:aspect_ratio] + assert_equal 5.166648, metadata[:duration] + assert_not_includes metadata, :angle + end + + test "analyzing a rotated video" do + blob = create_file_blob(filename: "rotated_video.mp4", content_type: "video/mp4") + metadata = blob.tap(&:analyze).metadata + + assert_equal 640, metadata[:width] + assert_equal 480, metadata[:height] + assert_equal [4, 3], metadata[:aspect_ratio] + assert_equal 5.227975, metadata[:duration] + assert_equal 90, metadata[:angle] + end + + test "analyzing a video without a video stream" do + blob = create_file_blob(filename: "video_without_video_stream.mp4", content_type: "video/mp4") + assert_equal({ "analyzed" => true }, blob.tap(&:analyze).metadata) + end +end diff --git a/activestorage/test/fixtures/files/rotated_video.mp4 b/activestorage/test/fixtures/files/rotated_video.mp4 new file mode 100644 index 0000000000..4c7a4e9e57 Binary files /dev/null and b/activestorage/test/fixtures/files/rotated_video.mp4 differ diff --git a/activestorage/test/fixtures/files/video_without_video_stream.mp4 b/activestorage/test/fixtures/files/video_without_video_stream.mp4 new file mode 100644 index 0000000000..e6a55f868b Binary files /dev/null and b/activestorage/test/fixtures/files/video_without_video_stream.mp4 differ diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index 379ae0a416..47f2bd7911 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -15,7 +15,7 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "funky.jpg", @user.avatar.filename.to_s end - test "attach existing sgid blob" do + test "attach existing blob from a signed ID" do @user.avatar.attach create_blob(filename: "funky.jpg").signed_id assert_equal "funky.jpg", @user.avatar.filename.to_s end @@ -63,6 +63,31 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "funky.jpg", @user.avatar_attachment.blob.filename.to_s end + test "analyze newly-attached blob" do + perform_enqueued_jobs do + @user.avatar.attach create_file_blob + end + + assert_equal 4104, @user.avatar.reload.metadata[:width] + assert_equal 2736, @user.avatar.metadata[:height] + end + + test "analyze attached blob only once" do + blob = create_file_blob + + perform_enqueued_jobs do + @user.avatar.attach blob + end + + assert blob.reload.analyzed? + + @user.avatar.attachment.destroy + + assert_no_enqueued_jobs do + @user.reload.avatar.attach blob + end + end + test "purge attached blob" do @user.avatar.attach create_blob(filename: "funky.jpg") avatar_key = @user.avatar.key @@ -135,6 +160,38 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "town.jpg", @user.highlights_attachments.first.blob.filename.to_s end + test "analyze newly-attached blobs" do + perform_enqueued_jobs do + @user.highlights.attach( + create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg"), + create_file_blob(filename: "video.mp4", content_type: "video/mp4")) + end + + assert_equal 4104, @user.highlights.first.metadata[:width] + assert_equal 2736, @user.highlights.first.metadata[:height] + + assert_equal 640, @user.highlights.second.metadata[:width] + assert_equal 480, @user.highlights.second.metadata[:height] + end + + test "analyze attached blobs only once" do + blobs = [ + create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg"), + create_file_blob(filename: "video.mp4", content_type: "video/mp4") + ] + + perform_enqueued_jobs do + @user.highlights.attach(blobs) + end + + assert blobs.each(&:reload).all?(&:analyzed?) + + @user.highlights.attachments.destroy_all + + assert_no_enqueued_jobs do + @user.highlights.attach(blobs) + end + end test "purge attached blobs" do @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg") diff --git a/activestorage/test/previewer/pdf_previewer_test.rb b/activestorage/test/previewer/pdf_previewer_test.rb index 60f075d1b2..fe32f39be4 100644 --- a/activestorage/test/previewer/pdf_previewer_test.rb +++ b/activestorage/test/previewer/pdf_previewer_test.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require "test_helper" +require "database/setup" + require "active_storage/previewer/pdf_previewer" class ActiveStorage::Previewer::PDFPreviewerTest < ActiveSupport::TestCase diff --git a/activestorage/test/previewer/video_previewer_test.rb b/activestorage/test/previewer/video_previewer_test.rb index 967d5d5ba9..dba9b0d7e2 100644 --- a/activestorage/test/previewer/video_previewer_test.rb +++ b/activestorage/test/previewer/video_previewer_test.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require "test_helper" +require "database/setup" + require "active_storage/previewer/video_previewer" class ActiveStorage::Previewer::VideoPreviewerTest < ActiveSupport::TestCase diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb index 60656feb80..38408cdad3 100644 --- a/activestorage/test/test_helper.rb +++ b/activestorage/test/test_helper.rb @@ -33,8 +33,8 @@ end require "tmpdir" ActiveStorage::Blob.service = ActiveStorage::Service::DiskService.new(root: Dir.mktmpdir("active_storage_tests")) -ActiveStorage::Service.logger = ActiveSupport::Logger.new(nil) +ActiveStorage.logger = ActiveSupport::Logger.new(nil) ActiveStorage.verifier = ActiveSupport::MessageVerifier.new("Testing") class ActiveSupport::TestCase @@ -46,9 +46,7 @@ class ActiveSupport::TestCase end def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg") - ActiveStorage::Blob.create_after_upload! \ - io: file_fixture(filename).open, - filename: filename, content_type: content_type + ActiveStorage::Blob.create_after_upload! io: file_fixture(filename).open, filename: filename, content_type: content_type end def create_blob_before_direct_upload(filename: "hello.txt", byte_size:, checksum:, content_type: "text/plain") -- cgit v1.2.3 From 398e4fecde6f8b3263cc7c04face664c248d3966 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Sun, 22 Oct 2017 13:36:37 -0400 Subject: Fix links [ci skip] --- activestorage/lib/active_storage/analyzer/image_analyzer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activestorage/lib/active_storage/analyzer/image_analyzer.rb b/activestorage/lib/active_storage/analyzer/image_analyzer.rb index b25d2092b9..25e0251e6e 100644 --- a/activestorage/lib/active_storage/analyzer/image_analyzer.rb +++ b/activestorage/lib/active_storage/analyzer/image_analyzer.rb @@ -8,8 +8,8 @@ module ActiveStorage # ActiveStorage::Analyzer::ImageAnalyzer.new(blob).metadata # # => { width: 4104, height: 2736 } # - # This analyzer relies on the third-party [MiniMagick]{https://github.com/minimagick/minimagick} gem. MiniMagick requires - # the [ImageMagick]{http://www.imagemagick.org} system library. These libraries are not provided by Rails; you must + # This analyzer relies on the third-party {MiniMagick}[https://github.com/minimagick/minimagick] gem. MiniMagick requires + # the {ImageMagick}[http://www.imagemagick.org] system library. These libraries are not provided by Rails; you must # install them yourself to use this analyzer. class Analyzer::ImageAnalyzer < Analyzer def self.accept?(blob) -- cgit v1.2.3 From 2b6cd16637ddfe099db157d3e02dd34e41ef9b5d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 23 Oct 2017 03:03:47 +0900 Subject: Place `MocktailDesigner` in `test/models/drink_designer.rb` Since `MocktailDesigner` inherits `DrinkDesigner` and can not be used alone. --- activerecord/test/cases/reflection_test.rb | 1 - activerecord/test/models/drink_designer.rb | 3 +++ activerecord/test/models/mocktail_designer.rb | 4 ---- 3 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 activerecord/test/models/mocktail_designer.rb diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index de1d244d65..0c522b8817 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -25,7 +25,6 @@ require "models/chef" require "models/department" require "models/cake_designer" require "models/drink_designer" -require "models/mocktail_designer" require "models/recipe" class ReflectionTest < ActiveRecord::TestCase diff --git a/activerecord/test/models/drink_designer.rb b/activerecord/test/models/drink_designer.rb index 1c407844c5..eb6701b84e 100644 --- a/activerecord/test/models/drink_designer.rb +++ b/activerecord/test/models/drink_designer.rb @@ -3,3 +3,6 @@ class DrinkDesigner < ActiveRecord::Base has_one :chef, as: :employable end + +class MocktailDesigner < DrinkDesigner +end diff --git a/activerecord/test/models/mocktail_designer.rb b/activerecord/test/models/mocktail_designer.rb deleted file mode 100644 index 123ff4fb3d..0000000000 --- a/activerecord/test/models/mocktail_designer.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class MocktailDesigner < DrinkDesigner -end -- cgit v1.2.3 From f4d1aa5310284ebceb51970140fa08f6c8ea19b6 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Sun, 22 Oct 2017 23:14:44 -0400 Subject: Use the indicative mood consistently [ci skip] --- activestorage/lib/active_storage/downloading.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/lib/active_storage/downloading.rb b/activestorage/lib/active_storage/downloading.rb index bcf42f610e..ceb7cce0c7 100644 --- a/activestorage/lib/active_storage/downloading.rb +++ b/activestorage/lib/active_storage/downloading.rb @@ -11,7 +11,7 @@ module ActiveStorage end end - # Efficiently download blob data into the given file. + # Efficiently downloads blob data into the given file. def download_blob_to(file) # :doc: file.binmode blob.download { |chunk| file.write(chunk) } -- cgit v1.2.3 From 813f8e333dabb2050d6b668b7bdd68b4979e8af4 Mon Sep 17 00:00:00 2001 From: Paul Kuruvilla Date: Mon, 23 Oct 2017 15:17:31 +0530 Subject: Fix #to_json for unreadable IO objects, fixes #26132 --- activesupport/CHANGELOG.md | 8 ++++++++ activesupport/lib/active_support/core_ext/object/json.rb | 6 ++++++ activesupport/test/json/encoding_test.rb | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 7696fdcd7a..4e713da8a8 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,11 @@ +* `IO#to_json` now returns the `to_s` representation, rather than + attempting to convert to an array. This fixes a bug where `IO#to_json` + would raise an `IOError` when called on an unreadable object. + + Fixes #26132. + + *Paul Kuruvilla* + * `Hash#slice` now falls back to Ruby 2.5+'s built-in definition if defined. *Akira Matsuda* diff --git a/activesupport/lib/active_support/core_ext/object/json.rb b/activesupport/lib/active_support/core_ext/object/json.rb index 0a3b875f24..f7c623fe13 100644 --- a/activesupport/lib/active_support/core_ext/object/json.rb +++ b/activesupport/lib/active_support/core_ext/object/json.rb @@ -135,6 +135,12 @@ module Enumerable end end +class IO + def as_json(options = nil) #:nodoc: + to_s + end +end + class Range def as_json(options = nil) #:nodoc: to_s diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index eafa2e1712..96ad8dfbdb 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -454,6 +454,10 @@ EXPECTED assert_equal '{"number":null}', NaNNumber.new.to_json end + def test_to_json_works_on_io_objects + assert_equal STDOUT.to_s.to_json, STDOUT.to_json + end + private def object_keys(json_object) -- cgit v1.2.3 From 9fedd013475456dc9bdbf0be45a194cc5fc2566e Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 23 Oct 2017 21:42:11 +0900 Subject: Ensure associations doesn't table name collide with string joins Currently we have no test for alias tracking with string joins. I've add test case for that to catch a future regression. --- activerecord/lib/active_record/associations/alias_tracker.rb | 10 ++++++---- .../test/cases/associations/inner_join_association_test.rb | 5 +++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index a8d8ee268a..6bac5fd3f5 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -19,14 +19,16 @@ module ActiveRecord end def self.initial_count_for(connection, name, table_joins) - # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase - quoted_name = connection.quote_table_name(name).downcase + quoted_name = nil counts = table_joins.map do |join| if join.is_a?(Arel::Nodes::StringJoin) + # quoted_name should be case ignored as some database adapters (Oracle) return quoted name in uppercase + quoted_name ||= connection.quote_table_name(name) + # Table names + table aliases - join.left.downcase.scan( - /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ + join.left.scan( + /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i ).size elsif join.respond_to? :left join.left.table_name == name ? 1 : 0 diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index 23be344419..d2494cabfa 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -27,6 +27,11 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase end end + def test_construct_finder_sql_does_not_table_name_collide_with_string_joins + sql = Person.joins(:agents).joins("JOIN people agents_people ON agents_people.primary_contact_id = people.id").to_sql + assert_match(/agents_people_2/i, sql) + end + def test_construct_finder_sql_ignores_empty_joins_hash sql = Author.joins({}).to_sql assert_no_match(/JOIN/i, sql) -- cgit v1.2.3 From 40cadf52db5e21349f6ecd9c4dc9b89d42ebc986 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 23 Oct 2017 22:20:58 +0900 Subject: Fix duplicate aliases when using both INNER/LEFT JOINs It should be shared the count of alias tracking in both INNER/LEFT JOINs to avoid duplicate aliases. Fixes #30504. Closes #30410. --- activerecord/lib/active_record/relation/query_methods.rb | 7 ++++--- .../test/cases/associations/inner_join_association_test.rb | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 269790cdba..897ff5c8af 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -924,7 +924,7 @@ module ActiveRecord def build_arel(aliases) arel = Arel::SelectManager.new(table) - build_joins(arel, joins_values.flatten, aliases) unless joins_values.empty? + aliases = build_joins(arel, joins_values.flatten, aliases) unless joins_values.empty? build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases) unless left_outer_joins_values.empty? arel.where(where_clause.ast) unless where_clause.empty? @@ -1011,9 +1011,10 @@ module ActiveRecord string_joins = buckets[:string_join].map(&:strip).uniq join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins) + alias_tracker = alias_tracker(join_list, aliases) join_dependency = ActiveRecord::Associations::JoinDependency.new( - klass, table, association_joins, alias_tracker(join_list, aliases) + klass, table, association_joins, alias_tracker ) joins = join_dependency.join_constraints(stashed_association_joins, join_type) @@ -1021,7 +1022,7 @@ module ActiveRecord manager.join_sources.concat(join_list) - manager + alias_tracker.aliases end def convert_join_strings_to_ast(table, joins) diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index d2494cabfa..eb85acf37e 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -27,6 +27,11 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase end end + def test_construct_finder_sql_does_not_table_name_collide_on_duplicate_associations_with_left_outer_joins + sql = Person.joins(agents: :agents).left_outer_joins(agents: :agents).to_sql + assert_match(/agents_people_4/i, sql) + end + def test_construct_finder_sql_does_not_table_name_collide_with_string_joins sql = Person.joins(:agents).joins("JOIN people agents_people ON agents_people.primary_contact_id = people.id").to_sql assert_match(/agents_people_2/i, sql) -- cgit v1.2.3 From 95de5033cded0b59372c0cb6a329a703c7b34905 Mon Sep 17 00:00:00 2001 From: Prathamesh Sonpatki Date: Mon, 23 Oct 2017 20:04:31 +0530 Subject: Remove CHANGELOT entry for the change that was backported to 5-1-stable [ci skip] - It was backported in https://github.com/rails/rails/commit/0eae8dd4b859c109919e5da0d6e74ffc6dc8a258 and is present in Rails 5.1.3 --- activejob/CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md index 425ab0c789..0c69a5c663 100644 --- a/activejob/CHANGELOG.md +++ b/activejob/CHANGELOG.md @@ -2,11 +2,4 @@ *Jeremy Daer* -* Change logging instrumentation to log errors when a job raises an exception. - - Fixes #26848. - - *Steven Bull* - - Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activejob/CHANGELOG.md) for previous changes. -- cgit v1.2.3 From 7de7f12fd140a60134defe7dc55b5a20b2372d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 17 Jul 2017 16:45:59 -0400 Subject: Remove deprecated Erubis ERB handler --- Gemfile | 3 - Gemfile.lock | 2 - actionview/CHANGELOG.md | 4 ++ .../lib/action_view/template/handlers/erb.rb | 3 - .../template/handlers/erb/deprecated_erubis.rb | 11 --- .../action_view/template/handlers/erb/erubis.rb | 83 ---------------------- .../erb/deprecated_erubis_implementation_test.rb | 15 ---- 7 files changed, 4 insertions(+), 117 deletions(-) delete mode 100644 actionview/lib/action_view/template/handlers/erb/deprecated_erubis.rb delete mode 100644 actionview/lib/action_view/template/handlers/erb/erubis.rb delete mode 100644 actionview/test/template/erb/deprecated_erubis_implementation_test.rb diff --git a/Gemfile b/Gemfile index 38125cf0cc..2704e1e806 100644 --- a/Gemfile +++ b/Gemfile @@ -51,9 +51,6 @@ gem "dalli", ">= 2.2.1" gem "listen", ">= 3.0.5", "< 3.2", require: false gem "libxml-ruby", platforms: :ruby -# Action View. For testing Erubis handler deprecation. -gem "erubis", "~> 2.7.0", require: false - # for railties app_generator_test gem "bootsnap", ">= 1.1.0", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 6ac4af557e..3301694415 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -253,7 +253,6 @@ GEM em-socksify (0.3.1) eventmachine (>= 1.0.0.beta.4) erubi (1.6.1) - erubis (2.7.0) et-orbi (1.0.5) tzinfo event_emitter (0.2.6) @@ -540,7 +539,6 @@ DEPENDENCIES delayed_job delayed_job_active_record em-hiredis - erubis (~> 2.7.0) google-cloud-storage (~> 1.3) hiredis json (>= 2.0.0) diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index b22686d1d8..e3227103d6 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated Erubis ERB handler. + + *Rafael Mendonça França* + * Remove default `alt` text generation. Fixes #30096 diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb index c41de62e52..b7b749f9da 100644 --- a/actionview/lib/action_view/template/handlers/erb.rb +++ b/actionview/lib/action_view/template/handlers/erb.rb @@ -3,11 +3,8 @@ module ActionView class Template module Handlers - autoload :Erubis, "action_view/template/handlers/erb/deprecated_erubis" - class ERB autoload :Erubi, "action_view/template/handlers/erb/erubi" - autoload :Erubis, "action_view/template/handlers/erb/erubis" # Specify trim mode for the ERB compiler. Defaults to '-'. # See ERB documentation for suitable values. diff --git a/actionview/lib/action_view/template/handlers/erb/deprecated_erubis.rb b/actionview/lib/action_view/template/handlers/erb/deprecated_erubis.rb deleted file mode 100644 index 00c7d7cc7d..0000000000 --- a/actionview/lib/action_view/template/handlers/erb/deprecated_erubis.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -::ActiveSupport::Deprecation.warn("ActionView::Template::Handlers::Erubis is deprecated and will be removed from Rails 5.2. Switch to ActionView::Template::Handlers::ERB::Erubi instead.") - -module ActionView - class Template - module Handlers - Erubis = ERB::Erubis - end - end -end diff --git a/actionview/lib/action_view/template/handlers/erb/erubis.rb b/actionview/lib/action_view/template/handlers/erb/erubis.rb deleted file mode 100644 index dc3bf1ff27..0000000000 --- a/actionview/lib/action_view/template/handlers/erb/erubis.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -gem "erubis" -require "erubis" - -module ActionView - class Template - module Handlers - class ERB - class Erubis < ::Erubis::Eruby - # :nodoc: all - def add_preamble(src) - @newline_pending = 0 - src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;" - end - - def add_text(src, text) - return if text.empty? - - if text == "\n" - @newline_pending += 1 - else - src << "@output_buffer.safe_append='" - src << "\n" * @newline_pending if @newline_pending > 0 - src << escape_text(text) - src << "'.freeze;" - - @newline_pending = 0 - end - end - - # Erubis toggles <%= and <%== behavior when escaping is enabled. - # We override to always treat <%== as escaped. - def add_expr(src, code, indicator) - case indicator - when "==" - add_expr_escaped(src, code) - else - super - end - end - - BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/ - - def add_expr_literal(src, code) - flush_newline_if_pending(src) - if BLOCK_EXPR.match?(code) - src << "@output_buffer.append= " << code - else - src << "@output_buffer.append=(" << code << ");" - end - end - - def add_expr_escaped(src, code) - flush_newline_if_pending(src) - if BLOCK_EXPR.match?(code) - src << "@output_buffer.safe_expr_append= " << code - else - src << "@output_buffer.safe_expr_append=(" << code << ");" - end - end - - def add_stmt(src, code) - flush_newline_if_pending(src) - super - end - - def add_postamble(src) - flush_newline_if_pending(src) - src << "@output_buffer.to_s" - end - - def flush_newline_if_pending(src) - if @newline_pending > 0 - src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;" - @newline_pending = 0 - end - end - end - end - end - end -end diff --git a/actionview/test/template/erb/deprecated_erubis_implementation_test.rb b/actionview/test/template/erb/deprecated_erubis_implementation_test.rb deleted file mode 100644 index ea088e7cfc..0000000000 --- a/actionview/test/template/erb/deprecated_erubis_implementation_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require "abstract_unit" - -module ERBTest - class DeprecatedErubisImplementationTest < ActionView::TestCase - test "Erubis implementation is deprecated" do - assert_deprecated "ActionView::Template::Handlers::Erubis is deprecated and will be removed from Rails 5.2. Switch to ActionView::Template::Handlers::ERB::Erubi instead." do - assert_equal "ActionView::Template::Handlers::ERB::Erubis", ActionView::Template::Handlers::Erubis.to_s - - assert_nothing_raised { Class.new(ActionView::Template::Handlers::Erubis) } - end - end - end -end -- cgit v1.2.3 From e16c765ac6dcff068ff2e5554d69ff345c003de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 17 Jul 2017 16:51:51 -0400 Subject: Remove deprecated `ActionController::ParamsParser::ParseError` --- actionpack/CHANGELOG.md | 8 ++++++-- actionpack/lib/action_dispatch/http/parameters.rb | 5 ----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index e5c814cc79..32239d202c 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated `ActionController::ParamsParser::ParseError`. + + *Rafael Mendonça França* + * Add `:allow_other_host` option to `redirect_back` method. When `allow_other_host` is set to `false`, the `redirect_back` will not allow a redirecting from a different host. @@ -82,7 +86,7 @@ *Kir Shatrov* -* `driven_by` now registers poltergeist and capybara-webkit +* `driven_by` now registers poltergeist and capybara-webkit. If poltergeist or capybara-webkit are set as drivers is set for System Tests, `driven_by` will register the driver and set additional options passed via @@ -92,7 +96,7 @@ *Mario Chavez* -* AEAD encrypted cookies and sessions with GCM +* AEAD encrypted cookies and sessions with GCM. Encrypted cookies now use AES-GCM which couples authentication and encryption in one faster step and produces shorter ciphertexts. Cookies diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index ae875eb830..8d7431fd6b 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -123,9 +123,4 @@ module ActionDispatch end end end - - module ParamsParser - include ActiveSupport::Deprecation::DeprecatedConstantAccessor - deprecate_constant "ParseError", "ActionDispatch::Http::Parameters::ParseError" - end end -- cgit v1.2.3 From 48766e32d31651606b9f68a16015ad05c3b0de2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 17 Jul 2017 17:12:59 -0400 Subject: Removed deprected evented redis adapter --- actioncable/CHANGELOG.md | 4 + actioncable/README.md | 2 +- .../subscription_adapter/evented_redis.rb | 89 ---------------------- .../subscription_adapter/evented_redis_test.rb | 61 --------------- 4 files changed, 5 insertions(+), 151 deletions(-) delete mode 100644 actioncable/lib/action_cable/subscription_adapter/evented_redis.rb delete mode 100644 actioncable/test/subscription_adapter/evented_redis_test.rb diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md index 3952887b61..4355c97df0 100644 --- a/actioncable/CHANGELOG.md +++ b/actioncable/CHANGELOG.md @@ -1,3 +1,7 @@ +* Removed deprecated evented redis adapter. + + *Rafael Mendonça França* + * Support redis-rb 4.0. *Jeremy Daer* diff --git a/actioncable/README.md b/actioncable/README.md index 70b39ead57..44fb81478d 100644 --- a/actioncable/README.md +++ b/actioncable/README.md @@ -446,7 +446,7 @@ The WebSocket server doesn't have access to the session, but it has access to th ## Dependencies -Action Cable provides a subscription adapter interface to process its pubsub internals. By default, asynchronous, inline, PostgreSQL, evented Redis, and non-evented Redis adapters are included. The default adapter in new Rails applications is the asynchronous (`async`) adapter. To create your own adapter, you can look at `ActionCable::SubscriptionAdapter::Base` for all methods that must be implemented, and any of the adapters included within Action Cable as example implementations. +Action Cable provides a subscription adapter interface to process its pubsub internals. By default, asynchronous, inline, PostgreSQL, and Redis adapters are included. The default adapter in new Rails applications is the asynchronous (`async`) adapter. To create your own adapter, you can look at `ActionCable::SubscriptionAdapter::Base` for all methods that must be implemented, and any of the adapters included within Action Cable as example implementations. The Ruby side of things is built on top of [websocket-driver](https://github.com/faye/websocket-driver-ruby), [nio4r](https://github.com/celluloid/nio4r), and [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby). diff --git a/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb b/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb deleted file mode 100644 index 1227c793a9..0000000000 --- a/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -require "thread" - -gem "em-hiredis", "~> 0.3.0" -gem "redis", ">= 3", "< 5" -require "em-hiredis" -require "redis" - -EventMachine.epoll if EventMachine.epoll? -EventMachine.kqueue if EventMachine.kqueue? - -module ActionCable - module SubscriptionAdapter - class EventedRedis < Base # :nodoc: - prepend ChannelPrefix - - @@mutex = Mutex.new - - # Overwrite this factory method for EventMachine Redis connections if you want to use a different Redis connection library than EM::Hiredis. - # This is needed, for example, when using Makara proxies for distributed Redis. - cattr_accessor :em_redis_connector, default: ->(config) { EM::Hiredis.connect(config[:url]) } - - # Overwrite this factory method for Redis connections if you want to use a different Redis connection library than Redis. - # This is needed, for example, when using Makara proxies for distributed Redis. - cattr_accessor :redis_connector, default: ->(config) { ::Redis.new(url: config[:url]) } - - def initialize(*) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - The "evented_redis" subscription adapter is deprecated and - will be removed in Rails 5.2. Please use the "redis" adapter - instead. - MSG - - super - @redis_connection_for_broadcasts = @redis_connection_for_subscriptions = nil - end - - def broadcast(channel, payload) - redis_connection_for_broadcasts.publish(channel, payload) - end - - def subscribe(channel, message_callback, success_callback = nil) - redis_connection_for_subscriptions.pubsub.subscribe(channel, &message_callback).tap do |result| - result.callback { |reply| success_callback.call } if success_callback - end - end - - def unsubscribe(channel, message_callback) - redis_connection_for_subscriptions.pubsub.unsubscribe_proc(channel, message_callback) - end - - def shutdown - redis_connection_for_subscriptions.pubsub.close_connection - @redis_connection_for_subscriptions = nil - end - - private - def redis_connection_for_subscriptions - ensure_reactor_running - @redis_connection_for_subscriptions || @server.mutex.synchronize do - @redis_connection_for_subscriptions ||= self.class.em_redis_connector.call(@server.config.cable).tap do |redis| - redis.on(:reconnect_failed) do - @logger.error "[ActionCable] Redis reconnect failed." - end - - redis.on(:failed) do - @logger.error "[ActionCable] Redis connection has failed." - end - end - end - end - - def redis_connection_for_broadcasts - @redis_connection_for_broadcasts || @server.mutex.synchronize do - @redis_connection_for_broadcasts ||= self.class.redis_connector.call(@server.config.cable) - end - end - - def ensure_reactor_running - return if EventMachine.reactor_running? && EventMachine.reactor_thread - @@mutex.synchronize do - Thread.new { EventMachine.run } unless EventMachine.reactor_running? - Thread.pass until EventMachine.reactor_running? && EventMachine.reactor_thread - end - end - end - end -end diff --git a/actioncable/test/subscription_adapter/evented_redis_test.rb b/actioncable/test/subscription_adapter/evented_redis_test.rb deleted file mode 100644 index e3e0a0c72a..0000000000 --- a/actioncable/test/subscription_adapter/evented_redis_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -require "test_helper" -require_relative "common" -require_relative "channel_prefix" - -class EventedRedisAdapterTest < ActionCable::TestCase - include CommonSubscriptionAdapterTest - include ChannelPrefixTest - - def setup - assert_deprecated do - super - end - - # em-hiredis is warning-rich - @previous_verbose, $VERBOSE = $VERBOSE, nil - end - - def teardown - super - - # Ensure EM is shut down before we re-enable warnings - EventMachine.reactor_thread.tap do |thread| - EventMachine.stop - thread.join - end - - $VERBOSE = @previous_verbose - end - - def test_slow_eventmachine - require "eventmachine" - require "thread" - - lock = Mutex.new - - EventMachine.singleton_class.class_eval do - alias_method :delayed_initialize_event_machine, :initialize_event_machine - define_method(:initialize_event_machine) do - lock.synchronize do - sleep 0.5 - delayed_initialize_event_machine - end - end - end - - test_basic_broadcast - ensure - lock.synchronize do - EventMachine.singleton_class.class_eval do - alias_method :initialize_event_machine, :delayed_initialize_event_machine - remove_method :delayed_initialize_event_machine - end - end - end - - def cable_config - { adapter: "evented_redis", url: "redis://:password@127.0.0.1:6379/12" } - end -end -- cgit v1.2.3 From 82472b3922bda2f337a79cef961b4760d04f9689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 17 Jul 2017 17:46:07 -0400 Subject: Remove deprecated support to `quoted_id` when typecasting an Active Record object --- activerecord/CHANGELOG.md | 4 +++ .../connection_adapters/abstract/quoting.rb | 17 ---------- activerecord/lib/active_record/sanitization.rb | 5 --- activerecord/test/cases/quoting_test.rb | 37 ---------------------- activerecord/test/cases/sanitize_test.rb | 12 ------- 5 files changed, 4 insertions(+), 71 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 06d52ee1fa..a8a0225640 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated support to `quoted_id` when typecasting an Active Record object. + + *Rafael Mendonça França* + * Fix `bin/rails db:setup` and `bin/rails db:test:prepare` create wrong ar_internal_metadata's data for a test database. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 9ad04c3216..92e46ccf9f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -11,19 +11,6 @@ module ActiveRecord def quote(value) value = id_value_for_database(value) if value.is_a?(Base) - if value.respond_to?(:quoted_id) - at = value.method(:quoted_id).source_location - at &&= " at %s:%d" % at - - owner = value.method(:quoted_id).owner.to_s - klass = value.class.to_s - klass += "(#{owner})" unless owner == klass - - ActiveSupport::Deprecation.warn \ - "Defining #quoted_id is deprecated and will be ignored in Rails 5.2. (defined on #{klass}#{at})" - return value.quoted_id - end - if value.respond_to?(:value_for_database) value = value.value_for_database end @@ -37,10 +24,6 @@ module ActiveRecord def type_cast(value, column = nil) value = id_value_for_database(value) if value.is_a?(Base) - if value.respond_to?(:quoted_id) && value.respond_to?(:id) - return value.id - end - if column value = type_cast_from_column(column, value) end diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 91a4f1fad6..3482d32059 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -207,10 +207,5 @@ module ActiveRecord end end end - - def quoted_id # :nodoc: - self.class.connection.quote(@attributes[self.class.primary_key].value_for_database) - end - deprecate :quoted_id end end diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb index 59d3bbb573..897d252cf8 100644 --- a/activerecord/test/cases/quoting_test.rb +++ b/activerecord/test/cases/quoting_test.rb @@ -83,23 +83,6 @@ module ActiveRecord end end - class QuotedOne - def quoted_id - 1 - end - end - class SubQuotedOne < QuotedOne - end - def test_quote_with_quoted_id - assert_deprecated(/defined on \S+::QuotedOne at .*quoting_test\.rb:[0-9]/) do - assert_equal 1, @quoter.quote(QuotedOne.new) - end - - assert_deprecated(/defined on \S+::SubQuotedOne\(\S+::QuotedOne\) at .*quoting_test\.rb:[0-9]/) do - assert_equal 1, @quoter.quote(SubQuotedOne.new) - end - end - def test_quote_nil assert_equal "NULL", @quoter.quote(nil) end @@ -207,26 +190,6 @@ module ActiveRecord obj = Class.new.new assert_raise(TypeError) { @conn.type_cast(obj) } end - - def test_type_cast_object_which_responds_to_quoted_id - quoted_id_obj = Class.new { - def quoted_id - "'zomg'" - end - - def id - 10 - end - }.new - assert_equal 10, @conn.type_cast(quoted_id_obj) - - quoted_id_obj = Class.new { - def quoted_id - "'zomg'" - end - }.new - assert_raise(TypeError) { @conn.type_cast(quoted_id_obj) } - end end class QuoteBooleanTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb index 082d663675..85a555fe35 100644 --- a/activerecord/test/cases/sanitize_test.rb +++ b/activerecord/test/cases/sanitize_test.rb @@ -153,18 +153,6 @@ class SanitizeTest < ActiveRecord::TestCase assert_equal "name=#{quoted_bambi_and_thumper}", bind("name=?", "Bambi\nand\nThumper".mb_chars) end - def test_bind_record - o = Class.new { - def quoted_id - 1 - end - }.new - assert_deprecated { assert_equal "1", bind("?", o) } - - os = [o] * 3 - assert_deprecated { assert_equal "1,1,1", bind("?", os) } - end - def test_named_bind_with_postgresql_type_casts l = Proc.new { bind(":a::integer '2009-01-01'::date", a: "10") } assert_nothing_raised(&l) -- cgit v1.2.3 From 8f5b34df81175e30f68879479243fbce966122d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 17 Jul 2017 17:58:56 -0400 Subject: Remove deprecated argument `default` from `index_name_exists?` --- activerecord/CHANGELOG.md | 4 ++++ .../connection_adapters/abstract/schema_statements.rb | 7 +------ .../connection_adapters/postgresql/schema_statements.rb | 7 +------ activerecord/test/cases/migration/index_test.rb | 6 ++---- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index a8a0225640..da3a3a597d 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated argument `default` from `index_name_exists?`. + + *Rafael Mendonça França* + * Remove deprecated support to `quoted_id` when typecasting an Active Record object. *Rafael Mendonça França* 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 24b2234c67..4169883443 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -806,12 +806,7 @@ module ActiveRecord end # Verifies the existence of an index with a given name. - def index_name_exists?(table_name, index_name, default = nil) - unless default.nil? - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing default to #index_name_exists? is deprecated without replacement. - MSG - end + def index_name_exists?(table_name, index_name) index_name = index_name.to_s indexes(table_name).detect { |i| i.name == index_name } 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 79cb4e276d..d90dc8a99c 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -66,12 +66,7 @@ module ActiveRecord end # Verifies existence of an index with a given name. - def index_name_exists?(table_name, index_name, default = nil) - unless default.nil? - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing default to #index_name_exists? is deprecated without replacement. - MSG - end + def index_name_exists?(table_name, index_name) table = quoted_scope(table_name) index = quoted_scope(index_name) diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb index bf1ebdb4c5..b25c6d84bc 100644 --- a/activerecord/test/cases/migration/index_test.rb +++ b/activerecord/test/cases/migration/index_test.rb @@ -33,10 +33,8 @@ module ActiveRecord connection.add_index(table_name, [:foo], name: "old_idx") connection.rename_index(table_name, "old_idx", "new_idx") - assert_deprecated do - assert_not connection.index_name_exists?(table_name, "old_idx", false) - assert connection.index_name_exists?(table_name, "new_idx", true) - end + assert_not connection.index_name_exists?(table_name, "old_idx") + assert connection.index_name_exists?(table_name, "new_idx") end def test_rename_index_too_long -- cgit v1.2.3 From e65aff70696be52b46ebe57207ebd8bb2cfcdbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 23 Aug 2017 16:07:48 -0400 Subject: Remove deprecated support to passing a class to `:class_name` on associations --- activerecord/CHANGELOG.md | 4 ++++ activerecord/lib/active_record/associations.rb | 4 ++-- activerecord/lib/active_record/reflection.rb | 9 +-------- .../has_and_belongs_to_many_associations_test.rb | 13 ------------- activerecord/test/cases/reflection_test.rb | 5 +++-- 5 files changed, 10 insertions(+), 25 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index da3a3a597d..7d0b75ddbf 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated support to passing a class to `:class_name` on associations. + + *Rafael Mendonça França* + * Remove deprecated argument `default` from `index_name_exists?`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 2723b3dc03..d1c2286227 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1848,7 +1848,7 @@ module ActiveRecord builder = Builder::HasAndBelongsToMany.new name, self, options - join_model = ActiveSupport::Deprecation.silence { builder.through_model } + join_model = builder.through_model const_set join_model.name, join_model private_constant join_model.name @@ -1877,7 +1877,7 @@ module ActiveRecord hm_options[k] = options[k] if options.key? k end - ActiveSupport::Deprecation.silence { has_many name, scope, hm_options, &extension } + has_many name, scope, hm_options, &extension _reflections[name.to_s].parent_reflection = habtm_reflection end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 97adfb4352..a60dad91d6 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -431,14 +431,7 @@ module ActiveRecord @association_scope_cache = Concurrent::Map.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 + raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string." 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 979dd986de..c817d7267b 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 @@ -88,12 +88,6 @@ class DeveloperWithSymbolClassName < Developer has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys end -ActiveSupport::Deprecation.silence do - class DeveloperWithConstantClassName < Developer - has_and_belongs_to_many :projects, class_name: ProjectWithSymbolsForKeys - end -end - class DeveloperWithExtendOption < Developer module NamedExtension def category @@ -954,13 +948,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase end end - def test_with_constant_class_name - assert_nothing_raised do - developer = DeveloperWithConstantClassName.new - developer.projects - end - end - def test_alternate_database professor = Professor.create(name: "Plum") course = Course.create(name: "Forensics") diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 0c522b8817..684d760da0 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -432,9 +432,10 @@ class ReflectionTest < ActiveRecord::TestCase end def test_class_for_class_name - assert_deprecated do - assert_predicate ActiveRecord::Reflection.create(:has_many, :clients, nil, { class_name: Client }, Firm), :validate? + error = assert_raises(ArgumentError) do + ActiveRecord::Reflection.create(:has_many, :clients, nil, { class_name: Client }, Firm) end + assert_equal "A class was passed to `:class_name` but we are expecting a string.", error.message end def test_join_table -- cgit v1.2.3 From 63cf15877bae859ff7b4ebaf05186f3ca79c1863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 23 Aug 2017 16:08:34 -0400 Subject: Rase when calling `lock!` in a dirty record --- activerecord/CHANGELOG.md | 4 ++++ .../lib/active_record/locking/pessimistic.rb | 9 +++++---- activerecord/test/cases/locking_test.rb | 22 +++++++++++----------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7d0b75ddbf..9a58ebd100 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Raises when calling `lock!` in a dirty record. + + *Rafael Mendonça França* + * Remove deprecated support to passing a class to `:class_name` on associations. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb index 72bccd4906..bb85c47e06 100644 --- a/activerecord/lib/active_record/locking/pessimistic.rb +++ b/activerecord/lib/active_record/locking/pessimistic.rb @@ -63,12 +63,13 @@ module ActiveRecord def lock!(lock = true) if persisted? if changed? - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Locking a record with unpersisted changes is deprecated and will raise an - exception in Rails 5.2. Use `save` to persist the changes, or `reload` to - discard them explicitly. + raise(<<-MSG.squish) + Locking a record with unpersisted changes is not supported. Use + `save` to persist the changes, or `reload` to discard them + explicitly. MSG end + reload(lock: lock) end self diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 743680ba92..6791d50940 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -565,18 +565,18 @@ unless in_memory_db? end end - # Locking a record reloads it. - def test_sane_lock_method + def test_lock_does_not_raise_when_the_object_is_not_dirty + person = Person.find 1 assert_nothing_raised do - Person.transaction do - person = Person.find 1 - old, person.first_name = person.first_name, "fooman" - # Locking a dirty record is deprecated - assert_deprecated do - person.lock! - end - assert_equal old, person.first_name - end + person.lock! + end + end + + def test_lock_raises_when_the_record_is_dirty + person = Person.find 1 + person.first_name = "fooman" + assert_raises(RuntimeError) do + person.lock! end end -- cgit v1.2.3 From c9660b5777707658c414b430753029cd9bc39934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 23 Aug 2017 17:47:04 -0400 Subject: Remove deprecated methods `initialize_schema_migrations_table` and `initialize_internal_metadata_table` --- activerecord/CHANGELOG.md | 4 ++++ .../connection_adapters/abstract/schema_statements.rb | 10 ---------- activerecord/test/cases/migration_test.rb | 5 ----- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 9a58ebd100..2d3897fa92 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated methods `initialize_schema_migrations_table` and `initialize_internal_metadata_table`. + + *Rafael Mendonça França* + * Raises when calling `lock!` in a dirty record. *Rafael Mendonça França* 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 4169883443..1970cf5de0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1015,16 +1015,6 @@ module ActiveRecord insert_versions_sql(versions) if versions.any? end - def initialize_schema_migrations_table # :nodoc: - ActiveRecord::SchemaMigration.create_table - end - deprecate :initialize_schema_migrations_table - - def initialize_internal_metadata_table # :nodoc: - ActiveRecord::InternalMetadata.create_table - end - deprecate :initialize_internal_metadata_table - def internal_string_options_for_primary_key # :nodoc: { primary_key: true } end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 07afa89779..3ea4b0782b 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1107,11 +1107,6 @@ class CopyMigrationsTest < ActiveRecord::TestCase assert_raise(ArgumentError) { ActiveRecord::Migration[1.0] } end - def test_deprecate_initialize_internal_tables - assert_deprecated { ActiveRecord::Base.connection.initialize_schema_migrations_table } - assert_deprecated { ActiveRecord::Base.connection.initialize_internal_metadata_table } - end - def test_deprecate_supports_migrations assert_deprecated { ActiveRecord::Base.connection.supports_migrations? } end -- cgit v1.2.3 From 9438c144b1893f2a59ec0924afe4d46bd8d5ffdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Sep 2017 13:01:00 -0300 Subject: Remove deprecated method `supports_migrations?` --- activerecord/CHANGELOG.md | 4 ++++ .../lib/active_record/connection_adapters/abstract_adapter.rb | 5 ----- activerecord/test/cases/migration_test.rb | 4 ---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 2d3897fa92..1b74c3fcbb 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated method `supports_migrations?`. + + *Rafael Mendonça França* + * Remove deprecated methods `initialize_schema_migrations_table` and `initialize_internal_metadata_table`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index f2fe4e2c30..ece01500e7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -196,11 +196,6 @@ module ActiveRecord self.class::ADAPTER_NAME end - def supports_migrations? # :nodoc: - true - end - deprecate :supports_migrations? - def supports_primary_key? # :nodoc: true end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 3ea4b0782b..e5db70a985 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1107,10 +1107,6 @@ class CopyMigrationsTest < ActiveRecord::TestCase assert_raise(ArgumentError) { ActiveRecord::Migration[1.0] } end - def test_deprecate_supports_migrations - assert_deprecated { ActiveRecord::Base.connection.supports_migrations? } - end - def test_deprecate_schema_migrations_table_name assert_deprecated { ActiveRecord::Migrator.schema_migrations_table_name } end -- cgit v1.2.3 From c56ff22fc6e97df4656ddc22909d9bf8b0c2cbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Sep 2017 13:04:08 -0300 Subject: Remove deprecated method `supports_primary_key?` --- activerecord/CHANGELOG.md | 4 ++++ .../lib/active_record/connection_adapters/abstract_adapter.rb | 5 ----- activerecord/test/cases/primary_keys_test.rb | 4 ---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 1b74c3fcbb..aac1296243 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated method `supports_primary_key?`. + + *Rafael Mendonça França* + * Remove deprecated method `supports_migrations?`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index ece01500e7..a848dacff3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -196,11 +196,6 @@ module ActiveRecord self.class::ADAPTER_NAME end - def supports_primary_key? # :nodoc: - true - end - deprecate :supports_primary_key? - # Does this adapter support DDL rollbacks in transactions? That is, would # CREATE TABLE or ALTER TABLE get rolled back by a transaction? def supports_ddl_transactions? diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index a36d786b40..7f6c2382ca 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -156,10 +156,6 @@ class PrimaryKeysTest < ActiveRecord::TestCase assert_nothing_raised { MixedCaseMonkey.find(1).destroy } end - def test_deprecate_supports_primary_key - assert_deprecated { ActiveRecord::Base.connection.supports_primary_key? } - end - def test_primary_key_returns_value_if_it_exists klass = Class.new(ActiveRecord::Base) do self.table_name = "developers" -- cgit v1.2.3 From 7df6e3f3cbdea9a0460ddbab445c81fbb1cfd012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Sep 2017 13:06:24 -0300 Subject: Remove deprecated method `ActiveRecord::Migrator.schema_migrations_table_name` --- activerecord/CHANGELOG.md | 4 ++++ activerecord/lib/active_record/migration.rb | 5 ----- activerecord/test/cases/migration_test.rb | 4 ---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index aac1296243..e94d165631 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated method `ActiveRecord::Migrator.schema_migrations_table_name`. + + *Rafael Mendonça França* + * Remove deprecated method `supports_primary_key?`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 8845e26ab7..1485687053 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1026,11 +1026,6 @@ module ActiveRecord new(:up, migrations(migrations_paths), nil) end - def schema_migrations_table_name - SchemaMigration.table_name - end - deprecate :schema_migrations_table_name - def get_all_versions(connection = Base.connection) if SchemaMigration.table_exists? SchemaMigration.all_versions.map(&:to_i) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index e5db70a985..b18af2ab55 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1106,8 +1106,4 @@ class CopyMigrationsTest < ActiveRecord::TestCase def test_unknown_migration_version_should_raise_an_argument_error assert_raise(ArgumentError) { ActiveRecord::Migration[1.0] } end - - def test_deprecate_schema_migrations_table_name - assert_deprecated { ActiveRecord::Migrator.schema_migrations_table_name } - end end -- cgit v1.2.3 From d6b779ecebe57f6629352c34bfd6c442ac8fba0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Sep 2017 13:51:54 -0300 Subject: Remove deprecated argument `name` from `#indexes` --- activerecord/CHANGELOG.md | 4 ++++ .../connection_adapters/abstract/schema_statements.rb | 2 +- .../connection_adapters/mysql/schema_statements.rb | 8 +------- .../connection_adapters/postgresql/schema_statements.rb | 10 +--------- .../connection_adapters/sqlite3/schema_statements.rb | 8 +------- activerecord/test/cases/adapters/postgresql/connection_test.rb | 2 +- .../test/cases/adapters/sqlite3/sqlite3_adapter_test.rb | 8 -------- 7 files changed, 9 insertions(+), 33 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index e94d165631..daa02ad96a 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated argument `name` from `#indexes`. + + *Rafael Mendonça França* + * Remove deprecated method `ActiveRecord::Migrator.schema_migrations_table_name`. *Rafael Mendonça França* 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 1970cf5de0..6698047ad6 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -79,7 +79,7 @@ module ActiveRecord end # Returns an array of indexes for the given table. - def indexes(table_name, name = nil) + def indexes(table_name) raise NotImplementedError, "#indexes is not implemented" end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb index 759493e3bd..a15c7d1787 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb @@ -5,13 +5,7 @@ module ActiveRecord module MySQL module SchemaStatements # :nodoc: # Returns an array of indexes for the given table. - def indexes(table_name, name = nil) - if name - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing name to #indexes is deprecated without replacement. - MSG - end - + def indexes(table_name) indexes = [] current_index = nil execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result| 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 d90dc8a99c..846e721983 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "active_support/core_ext/string/strip" - module ActiveRecord module ConnectionAdapters module PostgreSQL @@ -84,13 +82,7 @@ module ActiveRecord 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 - + def indexes(table_name) # :nodoc: scope = quoted_scope(table_name) result = query(<<-SQL, "SCHEMA") diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb index f4e55147df..f0d702136d 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb @@ -5,13 +5,7 @@ module ActiveRecord module SQLite3 module SchemaStatements # :nodoc: # Returns an array of indexes for the given table. - def indexes(table_name, name = nil) - if name - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing name to #indexes is deprecated without replacement. - MSG - end - + def indexes(table_name) exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row| index_sql = query_value(<<-SQL, "SCHEMA") SELECT sql diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 2bb217a8b1..81358b8fc4 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -103,7 +103,7 @@ module ActiveRecord end def test_indexes_logs_name - assert_deprecated { @connection.indexes("items", "hello") } + @connection.indexes("items") assert_equal "SCHEMA", @subscriber.logged[0][1] end diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 2b51a32db6..1f057fe5c6 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -269,14 +269,6 @@ module ActiveRecord end end - def test_indexes_logs_name - with_example_table do - assert_logged [["PRAGMA index_list(\"ex\")", "SCHEMA", []]] do - assert_deprecated { @conn.indexes("ex", "hello") } - end - end - end - def test_table_exists_logs_name with_example_table do sql = <<-SQL -- cgit v1.2.3 From 9c6ee1bed0292fc32c23dc1c68951ae64fc510be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Sep 2017 13:56:25 -0300 Subject: Remove deprecated arguments from `#verify!` --- activerecord/CHANGELOG.md | 4 ++++ .../active_record/connection_adapters/abstract_adapter.rb | 5 +---- activerecord/test/cases/adapters/mysql2/connection_test.rb | 12 ------------ 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index daa02ad96a..abafe524f6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated arguments from `#verify!`. + + *Rafael Mendonça França* + * Remove deprecated argument `name` from `#indexes`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index a848dacff3..e3aab8dad8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -392,10 +392,7 @@ module ActiveRecord # Checks whether the connection to the database is still active (i.e. not stale). # This is done under the hood by calling #active?. If the connection # is no longer active, then this method will reconnect to the database. - def verify!(*ignored) - if ignored.size > 0 - ActiveSupport::Deprecation.warn("Passing arguments to #verify method of the connection has no effect and has been deprecated. Please remove all arguments from the #verify method call.") - end + def verify! reconnect! unless active? end diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 9d81d506a0..e61c70848a 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -65,18 +65,6 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase assert @connection.active? end - def test_verify_with_args_is_deprecated - assert_deprecated do - @connection.verify!(option: true) - end - assert_deprecated do - @connection.verify!([]) - end - assert_deprecated do - @connection.verify!({}) - end - end - def test_execute_after_disconnect @connection.disconnect! -- cgit v1.2.3 From e1066f450d1a99c9a0b4d786b202e2ca82a4c3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Sep 2017 14:13:04 -0300 Subject: Remove deprecated configuration `.error_on_ignored_order_or_limit` --- activerecord/CHANGELOG.md | 4 ++++ activerecord/lib/active_record/core.rb | 20 -------------------- activerecord/test/cases/batches_test.rb | 28 ---------------------------- 3 files changed, 4 insertions(+), 48 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index abafe524f6..4f21915d5e 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated configuration `.error_on_ignored_order_or_limit`. + + *Rafael Mendonça França* + * Remove deprecated arguments from `#verify!`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 945c4eca78..0f7a503c90 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -76,26 +76,6 @@ module ActiveRecord # scope being ignored is error-worthy, rather than a warning. mattr_accessor :error_on_ignored_order, instance_writer: false, default: false - def self.error_on_ignored_order_or_limit - ActiveSupport::Deprecation.warn(<<-MSG.squish) - The flag error_on_ignored_order_or_limit is deprecated. Limits are - now supported. Please use error_on_ignored_order instead. - MSG - error_on_ignored_order - end - - def error_on_ignored_order_or_limit - self.class.error_on_ignored_order_or_limit - end - - def self.error_on_ignored_order_or_limit=(value) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - The flag error_on_ignored_order_or_limit is deprecated. Limits are - now supported. Please use error_on_ignored_order= instead. - MSG - self.error_on_ignored_order = value - end - ## # :singleton-method: # Specify whether or not to use timestamps for migration versions diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index c965404b07..be8aeed5ac 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -586,34 +586,6 @@ class EachTest < ActiveRecord::TestCase end end - test ".error_on_ignored_order_or_limit= is deprecated" do - begin - prev = ActiveRecord::Base.error_on_ignored_order - assert_deprecated "Please use error_on_ignored_order= instead." do - ActiveRecord::Base.error_on_ignored_order_or_limit = true - end - assert ActiveRecord::Base.error_on_ignored_order - ensure - ActiveRecord::Base.error_on_ignored_order = prev - end - end - - test ".error_on_ignored_order_or_limit is deprecated" do - expected = ActiveRecord::Base.error_on_ignored_order - actual = assert_deprecated "Please use error_on_ignored_order instead." do - ActiveRecord::Base.error_on_ignored_order_or_limit - end - assert_equal expected, actual - end - - test "#error_on_ignored_order_or_limit is deprecated" do - expected = ActiveRecord::Base.error_on_ignored_order - actual = assert_deprecated "Please use error_on_ignored_order instead." do - Post.new.error_on_ignored_order_or_limit - end - assert_equal expected, actual - end - test ".find_each respects table alias" do assert_queries(1) do table_alias = Post.arel_table.alias("omg_posts") -- cgit v1.2.3 From ef7784752c5c5efbe23f62d2bbcc62d4fd8aacab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Sep 2017 14:20:50 -0300 Subject: Remove deprecated methd `#scope_chain` --- activerecord/CHANGELOG.md | 4 +++ activerecord/lib/active_record/reflection.rb | 6 ----- activerecord/test/cases/reflection_test.rb | 38 ---------------------------- 3 files changed, 4 insertions(+), 44 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 4f21915d5e..31d360b01e 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated method `#scope_chain`. + + *Rafael Mendonça França* + * Remove deprecated configuration `.error_on_ignored_order_or_limit`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index a60dad91d6..8877f762b2 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "active_support/core_ext/string/filters" -require "active_support/deprecation" require "concurrent/map" module ActiveRecord @@ -174,11 +173,6 @@ module ActiveRecord scope ? [scope] : [] end - def scope_chain - chain.map(&:scopes) - end - deprecate :scope_chain - def build_join_constraint(table, foreign_table) key = join_keys.key foreign_key = join_keys.foreign_key diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 684d760da0..37c2235f1a 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -253,32 +253,6 @@ class ReflectionTest < ActiveRecord::TestCase assert_equal expected, actual end - def test_scope_chain - expected = [ - [Tagging.reflect_on_association(:tag).scope, Post.reflect_on_association(:first_blue_tags).scope], - [Post.reflect_on_association(:first_taggings).scope], - [Author.reflect_on_association(:misc_posts).scope] - ] - actual = assert_deprecated do - Author.reflect_on_association(:misc_post_first_blue_tags).scope_chain - end - assert_equal expected, actual - - expected = [ - [ - Tagging.reflect_on_association(:blue_tag).scope, - Post.reflect_on_association(:first_blue_tags_2).scope, - Author.reflect_on_association(:misc_post_first_blue_tags_2).scope - ], - [], - [] - ] - actual = assert_deprecated do - Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain - end - assert_equal expected, actual - end - def test_scope_chain_does_not_interfere_with_hmt_with_polymorphic_case @hotel = Hotel.create! @department = @hotel.departments.create! @@ -415,18 +389,6 @@ class ReflectionTest < ActiveRecord::TestCase assert_equal "category_id", Post.reflect_on_association(:categorizations).foreign_key.to_s end - def test_through_reflection_scope_chain_does_not_modify_other_reflections - orig_conds = assert_deprecated do - Post.reflect_on_association(:first_blue_tags_2).scope_chain - end.inspect - assert_deprecated do - Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain - end - assert_equal orig_conds, assert_deprecated { - Post.reflect_on_association(:first_blue_tags_2).scope_chain - }.inspect - end - def test_symbol_for_class_name assert_equal Client, Firm.reflect_on_association(:unsorted_clients_with_symbol).klass end -- cgit v1.2.3 From 8f5413b896099f80ef46a97819fe47a820417bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Sep 2017 14:40:42 -0300 Subject: Remove deprecated method `#sanitize_conditions` --- activerecord/CHANGELOG.md | 4 ++++ activerecord/lib/active_record/sanitization.rb | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 31d360b01e..ee73004810 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated method `#sanitize_conditions`. + + *Rafael Mendonça França* + * Remove deprecated method `#scope_chain`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 3482d32059..1c3099f55c 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -30,8 +30,6 @@ module ActiveRecord end end alias :sanitize_sql :sanitize_sql_for_conditions - alias :sanitize_conditions :sanitize_sql - deprecate sanitize_conditions: :sanitize_sql # Accepts an array, hash, or string of SQL conditions and sanitizes # them into a valid SQL fragment for a SET clause. -- cgit v1.2.3 From 216965e926d30863a6338351fd13e939c3e72dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 19 Oct 2017 15:52:44 -0400 Subject: Change the deprecation horizon of the dynamic routes segment to 6.0 --- actionpack/lib/action_dispatch/routing/route_set.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 18862d819f..71cb458112 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -584,14 +584,14 @@ module ActionDispatch if route.segment_keys.include?(:controller) ActiveSupport::Deprecation.warn(<<-MSG.squish) Using a dynamic :controller segment in a route is deprecated and - will be removed in Rails 5.2. + will be removed in Rails 6.0. MSG end if route.segment_keys.include?(:action) ActiveSupport::Deprecation.warn(<<-MSG.squish) Using a dynamic :action segment in a route is deprecated and - will be removed in Rails 5.2. + will be removed in Rails 6.0. MSG end -- cgit v1.2.3 From c792354adcbf8c966f274915c605c6713b840548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 19 Oct 2017 16:17:48 -0400 Subject: Remove deprecated `:if` and `:unless` string filter for callbacks --- .../validations/conditional_validation_test.rb | 66 ---------------------- .../test/cases/validations/with_validation_test.rb | 40 +------------ activerecord/lib/active_record/callbacks.rb | 4 +- activerecord/lib/active_record/transactions.rb | 2 +- activesupport/CHANGELOG.md | 4 ++ activesupport/lib/active_support/callbacks.rb | 55 ++++++++---------- activesupport/test/callbacks_test.rb | 22 +++----- 7 files changed, 38 insertions(+), 155 deletions(-) diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb index 68dade556c..aa027c4128 100644 --- a/activemodel/test/cases/validations/conditional_validation_test.rb +++ b/activemodel/test/cases/validations/conditional_validation_test.rb @@ -43,48 +43,6 @@ class ConditionalValidationTest < ActiveModel::TestCase assert_equal ["hoo 5"], t.errors["title"] end - def test_if_validation_using_string_true - # When the evaluated string returns true - ActiveSupport::Deprecation.silence do - Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: "a = 1; a == 1") - end - t = Topic.new("title" => "uhohuhoh", "content" => "whatever") - assert t.invalid? - assert t.errors[:title].any? - assert_equal ["hoo 5"], t.errors["title"] - end - - def test_unless_validation_using_string_true - # When the evaluated string returns true - ActiveSupport::Deprecation.silence do - Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: "a = 1; a == 1") - end - t = Topic.new("title" => "uhohuhoh", "content" => "whatever") - assert t.valid? - assert_empty t.errors[:title] - end - - def test_if_validation_using_string_false - # When the evaluated string returns false - ActiveSupport::Deprecation.silence do - Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: "false") - end - t = Topic.new("title" => "uhohuhoh", "content" => "whatever") - assert t.valid? - assert_empty t.errors[:title] - end - - def test_unless_validation_using_string_false - # When the evaluated string returns false - ActiveSupport::Deprecation.silence do - Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: "false") - end - t = Topic.new("title" => "uhohuhoh", "content" => "whatever") - assert t.invalid? - assert t.errors[:title].any? - assert_equal ["hoo 5"], t.errors["title"] - end - def test_if_validation_using_block_true # When the block returns true Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", @@ -122,28 +80,4 @@ class ConditionalValidationTest < ActiveModel::TestCase assert t.errors[:title].any? assert_equal ["hoo 5"], t.errors["title"] end - - # previous implementation of validates_presence_of eval'd the - # string with the wrong binding, this regression test is to - # ensure that it works correctly - def test_validation_with_if_as_string - Topic.validates_presence_of(:title) - ActiveSupport::Deprecation.silence do - Topic.validates_presence_of(:author_name, if: "title.to_s.match('important')") - end - - t = Topic.new - assert t.invalid?, "A topic without a title should not be valid" - assert_empty t.errors[:author_name], "A topic without an 'important' title should not require an author" - - t.title = "Just a title" - assert t.valid?, "A topic with a basic title should be valid" - - t.title = "A very important title" - assert t.invalid?, "A topic with an important title, but without an author, should not be valid" - assert t.errors[:author_name].any?, "A topic with an 'important' title should require an author" - - t.author_name = "Hubert J. Farnsworth" - assert t.valid?, "A topic with an important title and author should be valid" - end end diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index fbe20dc000..13ef5e6a31 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -70,51 +70,15 @@ class ValidatesWithTest < ActiveModel::TestCase assert_includes topic.errors[:base], OTHER_ERROR_MESSAGE end - test "with if statements that return false" do - ActiveSupport::Deprecation.silence do - Topic.validates_with(ValidatorThatAddsErrors, if: "1 == 2") - end - topic = Topic.new - assert topic.valid? - end - - test "with if statements that return true" do - ActiveSupport::Deprecation.silence do - Topic.validates_with(ValidatorThatAddsErrors, if: "1 == 1") - end - topic = Topic.new - assert topic.invalid? - assert_includes topic.errors[:base], ERROR_MESSAGE - end - - test "with unless statements that return true" do - ActiveSupport::Deprecation.silence do - Topic.validates_with(ValidatorThatAddsErrors, unless: "1 == 1") - end - topic = Topic.new - assert topic.valid? - end - - test "with unless statements that returns false" do - ActiveSupport::Deprecation.silence do - Topic.validates_with(ValidatorThatAddsErrors, unless: "1 == 2") - end - topic = Topic.new - assert topic.invalid? - assert_includes topic.errors[:base], ERROR_MESSAGE - end - test "passes all configuration options to the validator class" do topic = Topic.new validator = Minitest::Mock.new - validator.expect(:new, validator, [{ foo: :bar, if: "1 == 1", class: Topic }]) + validator.expect(:new, validator, [{ foo: :bar, if: :condition_is_true, class: Topic }]) validator.expect(:validate, nil, [topic]) validator.expect(:is_a?, false, [Symbol]) validator.expect(:is_a?, false, [String]) - ActiveSupport::Deprecation.silence do - Topic.validates_with(validator, if: "1 == 1", foo: :bar) - end + Topic.validates_with(validator, if: :condition_is_true, foo: :bar) assert topic.valid? validator.verify end diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index ac1070e65b..9ab2780760 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -98,9 +98,9 @@ module ActiveRecord # == Types of callbacks # # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects, - # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects + # inline methods (using a proc). Method references and callback objects # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for - # creating mix-ins), and inline eval methods are deprecated. + # creating mix-ins). # # The method reference callbacks work by specifying a protected or private method available in the object, like this: # diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 6761f2da25..97cba5d1c7 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -285,7 +285,7 @@ module ActiveRecord fire_on = Array(options[:on]) assert_valid_transaction_action(fire_on) options[:if] = Array(options[:if]) - options[:if].unshift("transaction_include_any_action?(#{fire_on})") + options[:if].unshift(-> { transaction_include_any_action?(fire_on) }) end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 7696fdcd7a..387eb4cbb3 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated `:if` and `:unless` string filter for callbacks. + + *Rafael Mendonça França* + * `Hash#slice` now falls back to Ruby 2.5+'s built-in definition if defined. *Akira Matsuda* diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index eb6a4c3ce7..0ed4681b7d 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -298,8 +298,8 @@ module ActiveSupport @kind = kind @filter = filter @key = compute_identifier filter - @if = Array(options[:if]) - @unless = Array(options[:unless]) + @if = check_conditionals(Array(options[:if])) + @unless = check_conditionals(Array(options[:unless])) end def filter; @key; end @@ -323,7 +323,7 @@ module ActiveSupport def duplicates?(other) case @filter - when Symbol, String + when Symbol matches?(other.kind, other.filter) else false @@ -350,9 +350,21 @@ module ActiveSupport end private + def check_conditionals(conditionals) + if conditionals.any? { |c| c.is_a?(String) } + raise ArgumentError, <<-MSG.squish + Passing string to be evaluated in :if and :unless conditional + options is not supported. Pass a symbol for an instance method, + or a lambda, proc or block, instead. + MSG + end + + conditionals + end + def compute_identifier(filter) case filter - when String, ::Proc + when ::Proc filter.object_id else filter @@ -427,7 +439,6 @@ module ActiveSupport # Filters support: # # Symbols:: A method to call. - # Strings:: Some content to evaluate. # Procs:: A proc to call with the object. # Objects:: An object with a before_foo method on it to call. # @@ -437,8 +448,6 @@ module ActiveSupport case filter when Symbol new(nil, filter, [], nil) - when String - new(nil, :instance_exec, [:value], compile_lambda(filter)) when Conditionals::Value new(filter, :call, [:target, :value], nil) when ::Proc @@ -455,10 +464,6 @@ module ActiveSupport new(filter, method_to_call, [:target], nil) end end - - def self.compile_lambda(filter) - eval("lambda { |value| #{filter} }") - end end # Execute before and after filters in a sequence instead of @@ -651,26 +656,17 @@ module ActiveSupport # # ===== Options # - # * :if - A symbol, a string (deprecated) or an array of symbols, - # each naming an instance method or a proc; the callback will be called - # only when they all return a true value. - # * :unless - A symbol, a string (deprecated) or an array of symbols, - # each naming an instance method or a proc; the callback will be called - # only when they all return a false value. + # * :if - A symbol or an array of symbols, each naming an instance + # method or a proc; the callback will be called only when they all return + # a true value. + # * :unless - A symbol or an array of symbols, each naming an + # instance method or a proc; the callback will be called only when they + # all return a false value. # * :prepend - If +true+, the callback will be prepended to the # existing chain rather than appended. def set_callback(name, *filter_list, &block) type, filters, options = normalize_callback_params(filter_list, block) - if options[:if].is_a?(String) || options[:unless].is_a?(String) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing string to be evaluated in :if and :unless conditional - options is deprecated and will be removed in Rails 5.2 without - replacement. Pass a symbol for an instance method, or a lambda, - proc or block, instead. - MSG - end - self_chain = get_callbacks name mapped = filters.map do |filter| Callback.build(self_chain, filter, type, options) @@ -695,13 +691,6 @@ module ActiveSupport def skip_callback(name, *filter_list, &block) type, filters, options = normalize_callback_params(filter_list, block) - if options[:if].is_a?(String) || options[:unless].is_a?(String) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing string to :if and :unless conditional options is deprecated - and will be removed in Rails 5.2 without replacement. - MSG - end - options[:raise] = true unless options.key?(:raise) __update_callbacks(name) do |target, chain| diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 3902e41a60..5f894db46f 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -193,13 +193,6 @@ module CallbacksTest before_save Proc.new { |r| r.history << "b00m" }, if: :no before_save Proc.new { |r| r.history << [:before_save, :symbol] }, unless: :no before_save Proc.new { |r| r.history << "b00m" }, unless: :yes - # string - ActiveSupport::Deprecation.silence do - before_save Proc.new { |r| r.history << [:before_save, :string] }, if: "yes" - before_save Proc.new { |r| r.history << "b00m" }, if: "no" - before_save Proc.new { |r| r.history << [:before_save, :string] }, unless: "no" - before_save Proc.new { |r| r.history << "b00m" }, unless: "yes" - end # Combined if and unless before_save Proc.new { |r| r.history << [:before_save, :combined_symbol] }, if: :yes, unless: :no before_save Proc.new { |r| r.history << "b00m" }, if: :yes, unless: :yes @@ -592,8 +585,6 @@ module CallbacksTest [:before_save, :proc], [:before_save, :symbol], [:before_save, :symbol], - [:before_save, :string], - [:before_save, :string], [:before_save, :combined_symbol], ], person.history end @@ -1182,14 +1173,15 @@ module CallbacksTest end end - class DeprecatedWarningTest < ActiveSupport::TestCase - def test_deprecate_string_conditional_options + class NotSupportedStringConditionalTest < ActiveSupport::TestCase + def test_string_conditional_options klass = Class.new(Record) - assert_deprecated { klass.before_save :tweedle, if: "true" } - assert_deprecated { klass.after_save :tweedle, unless: "false" } - assert_deprecated { klass.skip_callback :save, :before, :tweedle, if: "true" } - assert_deprecated { klass.skip_callback :save, :after, :tweedle, unless: "false" } + assert_raises(ArgumentError) { klass.before_save :tweedle, if: ["true"] } + assert_raises(ArgumentError) { klass.before_save :tweedle, if: "true" } + assert_raises(ArgumentError) { klass.after_save :tweedle, unless: "false" } + assert_raises(ArgumentError) { klass.skip_callback :save, :before, :tweedle, if: "true" } + assert_raises(ArgumentError) { klass.skip_callback :save, :after, :tweedle, unless: "false" } end end -- cgit v1.2.3 From 19fbbebb1665e482d76cae30166b46e74ceafe29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 19 Oct 2017 16:22:41 -0400 Subject: Remove deprecated `halt_callback_chains_on_return_false` option --- activesupport/CHANGELOG.md | 4 ++++ activesupport/lib/active_support.rb | 12 ------------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 387eb4cbb3..f57b9ce5ab 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated `halt_callback_chains_on_return_false` option. + + *Rafael Mendonça França* + * Remove deprecated `:if` and `:unless` string filter for callbacks. *Rafael Mendonça França* diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index c75b5d630c..68be94f99d 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -82,18 +82,6 @@ module ActiveSupport cattr_accessor :test_order # :nodoc: - def self.halt_callback_chains_on_return_false - ActiveSupport::Deprecation.warn(<<-MSG.squish) - ActiveSupport.halt_callback_chains_on_return_false is deprecated and will be removed in Rails 5.2. - MSG - end - - def self.halt_callback_chains_on_return_false=(value) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - ActiveSupport.halt_callback_chains_on_return_false= is deprecated and will be removed in Rails 5.2. - MSG - end - def self.to_time_preserves_timezone DateAndTime::Compatibility.preserve_timezone end -- cgit v1.2.3 From fea8d3509d65396f7c04a5e2608ef8863e1abe75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 20 Oct 2017 11:49:03 -0400 Subject: Remove text about deprecation that was already removed --- activerecord/lib/active_record/associations.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d1c2286227..573f41d7ad 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -557,9 +557,8 @@ module ActiveRecord # has_many :birthday_events, ->(user) { where(starts_on: user.birthday) }, class_name: 'Event' # end # - # Note: Joining, eager loading and preloading of these associations is not fully possible. + # Note: Joining, eager loading and preloading of these associations is not possible. # These operations happen before instance creation and the scope will be called with a +nil+ argument. - # This can lead to unexpected behavior and is deprecated. # # == Association callbacks # -- cgit v1.2.3 From b6a0e43216653f1ed1dcd3c6ac8cb7f73297c6da Mon Sep 17 00:00:00 2001 From: Joe Francis Date: Mon, 23 Oct 2017 13:01:42 -0500 Subject: specify minimum capybara version for system tests Upgraded rails applications may have a Gemfile without a new enough capybara to run system tests. Setting a version here gives the user a more direct error message than they get otherwise. Resolves #30952 --- actionpack/lib/action_dispatch/system_test_case.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb index 496328bd1d..58cea7b779 100644 --- a/actionpack/lib/action_dispatch/system_test_case.rb +++ b/actionpack/lib/action_dispatch/system_test_case.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +gem "capybara", "~> 2.13" + require "capybara/dsl" require "capybara/minitest" require "action_controller" -- cgit v1.2.3 From 27394404390348c894ee9a527d786031ac0d5794 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Mon, 23 Oct 2017 21:18:57 +0300 Subject: Remove mention about Evented Redis [ci skip] Evented Redis is removed from Rails. See #30945 --- guides/source/action_cable_overview.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md index dd16ba3932..6ac6792e26 100644 --- a/guides/source/action_cable_overview.md +++ b/guides/source/action_cable_overview.md @@ -555,8 +555,7 @@ The async adapter is intended for development/testing and should not be used in ##### Redis Adapter -Action Cable contains two Redis adapters: "normal" Redis and Evented Redis. Both -of the adapters require users to provide a URL pointing to the Redis server. +The Redis adapter requires users to provide a URL pointing to the Redis server. Additionally, a `channel_prefix` may be provided to avoid channel name collisions when using the same Redis server for multiple applications. See the [Redis PubSub documentation](https://redis.io/topics/pubsub#database-amp-scoping) for more details. -- cgit v1.2.3 From a5c9f24603da478366a7fa1a1279f13501e30bbe Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Mon, 23 Oct 2017 11:55:12 -0700 Subject: Performance improvements for acts_like? method. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit activesupport/lib/active_support/core_ext/object/acts_like.rb acts_like? Add a case statement to use direct symbols instead of string interpolation for the three scenarios I found in the Rails codebase: time, date, and string. For time/date/string, this change prevents two string allocations for each time the method is called and speeds up the method by ~2.7x. For other arguments, there is no memory difference and performance difference is within margin of error. begin require "bundler/inline" rescue LoadError => e $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler" raise e end gemfile(true) do source "https://rubygems.org" gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" gem "benchmark-ips" end def allocate_count GC.disable before = ObjectSpace.count_objects yield after = ObjectSpace.count_objects after.each { |k,v| after[k] = v - before[k] } after[:T_HASH] -= 1 # probe effect - we created the before hash. GC.enable result = after.reject { |k,v| v == 0 } GC.start result end class Object def fast_acts_like?(duck) case duck when :time respond_to? :acts_like_time? when :date respond_to? :acts_like_date? when :string respond_to? :acts_like_string? else respond_to? :"acts_like_#{duck}?" end end end puts puts " acts_like? ".center(80, '=') puts obj = ''.freeze %i(time date string super_hacka).each do |type| puts " #{type} ".center(80, '=') puts " Memory Usage ".center(80, "=") puts puts "value.acts_like?" puts allocate_count { 1000.times { obj.acts_like?(type) } } puts "value.fast_acts_like?" puts allocate_count { 1000.times { obj.fast_acts_like?(type) } } puts puts " Benchmark.ips ".center(80, "=") puts Benchmark.ips do |x| x.report("acts_like?") { obj.acts_like?(type) } x.report("fast_acts_like?") { obj.fast_acts_like?(type) } x.compare! end end ================================== acts_like? ================================== ===================================== time ===================================== ================================= Memory Usage ================================= value.acts_like? {:FREE=>-1983, :T_STRING=>2052, :T_IMEMO=>1} value.fast_acts_like? {:FREE=>-1} ================================ Benchmark.ips ================================= Warming up -------------------------------------- acts_like? 104.281k i/100ms fast_acts_like? 155.523k i/100ms Calculating ------------------------------------- acts_like? 1.688M (±10.7%) i/s - 8.342M in 5.003804s fast_acts_like? 4.596M (±12.1%) i/s - 22.551M in 5.000124s Comparison: fast_acts_like?: 4596162.4 i/s acts_like?: 1688163.8 i/s - 2.72x slower ===================================== date ===================================== ================================= Memory Usage ================================= value.acts_like? {:FREE=>-2001, :T_STRING=>2000} value.fast_acts_like? {:FREE=>-1} ================================ Benchmark.ips ================================= Warming up -------------------------------------- acts_like? 85.372k i/100ms fast_acts_like? 166.097k i/100ms Calculating ------------------------------------- acts_like? 1.720M (± 8.3%) i/s - 8.537M in 5.001003s fast_acts_like? 4.695M (±10.1%) i/s - 23.254M in 5.010734s Comparison: fast_acts_like?: 4695493.1 i/s acts_like?: 1719637.9 i/s - 2.73x slower ==================================== string ==================================== ================================= Memory Usage ================================= value.acts_like? {:FREE=>-2001, :T_STRING=>2000} value.fast_acts_like? {:FREE=>-1} ================================ Benchmark.ips ================================= Warming up -------------------------------------- acts_like? 100.221k i/100ms fast_acts_like? 182.841k i/100ms Calculating ------------------------------------- acts_like? 1.706M (± 7.3%) i/s - 8.519M in 5.022331s fast_acts_like? 3.968M (±22.8%) i/s - 18.650M in 5.006762s Comparison: fast_acts_like?: 3967972.9 i/s acts_like?: 1705773.7 i/s - 2.33x slower ================================= super_hacka ================================== ================================= Memory Usage ================================= value.acts_like? {:FREE=>-2004, :T_STRING=>2002, :T_SYMBOL=>1} value.fast_acts_like? {:FREE=>-2003, :T_STRING=>2001, :T_SYMBOL=>1} ================================ Benchmark.ips ================================= Warming up -------------------------------------- acts_like? 100.344k i/100ms fast_acts_like? 101.690k i/100ms Calculating ------------------------------------- acts_like? 1.617M (± 7.5%) i/s - 8.128M in 5.055285s fast_acts_like? 1.534M (±10.1%) i/s - 7.627M in 5.031052s Comparison: acts_like?: 1617390.7 i/s fast_acts_like?: 1533897.3 i/s - same-ish: difference falls within error --- activesupport/lib/active_support/core_ext/object/acts_like.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/object/acts_like.rb b/activesupport/lib/active_support/core_ext/object/acts_like.rb index 2eb72f6b3a..403ee20e39 100644 --- a/activesupport/lib/active_support/core_ext/object/acts_like.rb +++ b/activesupport/lib/active_support/core_ext/object/acts_like.rb @@ -7,6 +7,15 @@ class Object # x.acts_like?(:date) to do duck-type-safe comparisons, since classes that # we want to act like Time simply need to define an acts_like_time? method. def acts_like?(duck) - respond_to? :"acts_like_#{duck}?" + case duck + when :time + respond_to? :acts_like_time? + when :date + respond_to? :acts_like_date? + when :string + respond_to? :acts_like_string? + else + respond_to? :"acts_like_#{duck}?" + end end end -- cgit v1.2.3 From 1015cab6e4db5f18deadba0a6eb2adbe82ffe585 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Mon, 23 Oct 2017 20:05:27 +0000 Subject: Remove `supports_disable_referential_integrity?` `supports_disable_referential_integrity?` used to handle if PostgreSQL database supports `ALTER TABLE DISABLE/ENABLE TRIGGER` statements. Refer https://github.com/rails/rails/commit/9a947af0e79cfb8692eb7e5ae94c1b8c40756f49 These statements have been documented since 8.1. https://www.postgresql.org/docs/8.1/static/sql-altertable.html > DISABLE/ENABLE TRIGGER Now Rails supports PostgreSQL 9.1 or higher only. No need to handle `supports_disable_referential_integrity?` anymore. Also, this method does not exist in any other adapters including AbstractAdapter. --- .../postgresql/referential_integrity.rb | 42 +++++++++------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb b/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb index 386d22a9bd..8df91c988b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb @@ -4,26 +4,21 @@ module ActiveRecord module ConnectionAdapters module PostgreSQL module ReferentialIntegrity # :nodoc: - def supports_disable_referential_integrity? # :nodoc: - true - end - def disable_referential_integrity # :nodoc: - if supports_disable_referential_integrity? - original_exception = nil + original_exception = nil - begin - transaction(requires_new: true) do - execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";")) - end - rescue ActiveRecord::ActiveRecordError => e - original_exception = e + begin + transaction(requires_new: true) do + execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";")) end + rescue ActiveRecord::ActiveRecordError => e + original_exception = e + end - begin - yield - rescue ActiveRecord::InvalidForeignKey => e - warn <<-WARNING + begin + yield + rescue ActiveRecord::InvalidForeignKey => e + warn <<-WARNING WARNING: Rails was not able to disable referential integrity. This is most likely caused due to missing permissions. @@ -32,17 +27,14 @@ Rails needs superuser privileges to disable referential integrity. cause: #{original_exception.try(:message)} WARNING - raise e - end + raise e + end - begin - transaction(requires_new: true) do - execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";")) - end - rescue ActiveRecord::ActiveRecordError + begin + transaction(requires_new: true) do + execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";")) end - else - yield + rescue ActiveRecord::ActiveRecordError end end end -- cgit v1.2.3 From a822fc513cb3c98207a14fc203c973e13f42e306 Mon Sep 17 00:00:00 2001 From: Nick LaMuro Date: Mon, 2 Oct 2017 16:03:49 -0500 Subject: Cache regexps generated from acronym_regex The Problem ----------- The following line from `String#camelize`: string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase } and the following line from `String#camelize`: word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }#{$2.downcase}" } Both generate the same regexep in the first part of the `.sub`/`.gsub` method calls every time the function is called, creating an extra object allocation each time. The value of `acronym_regex` only changes if the user decides add an acronym to the current set of inflections and apends another string on the the regexp generated here, but beyond that it remains relatively static. This has been around since acronym support was introduced back in 2011 in PR#1648. Proposed Solution ----------------- To avoid re-generating these strings every time these methods are called, cache the values of these regular expressions in the `ActiveSupport::Inflector::Inflections` instance, making it so these regular expressions are only generated once, or when the acronym's are added to. Other notable changes is the attr_readers are nodoc'd, as they shouldn't really be public APIs for users. Also, the new method, define_acronym_regex_patterns, is the only method in charge of manipulating @acronym_regex, and initialize_dup also makes use of that new change. ** Note about fix for non-deterministic actionpack test ** With the introduction of `@acronym_underscore_regex` and `@acronym_camelize_regex`, tests that manipulated these for a short time, then reset them could caused test failures to happen. This happened because the previous way we reset the `@acronyms` and `@acronym_regex` was the set them using #instance_variable_set, which wouldn't run the #define_acronym_regex_patterns method. This has now been introduced into the actionpack tests to avoid this failure. --- actionpack/test/dispatch/request_test.rb | 2 +- .../lib/active_support/inflector/inflections.rb | 20 ++++++++++++++++---- .../lib/active_support/inflector/methods.rb | 4 ++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 2a18395aac..2057aecbf9 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -777,7 +777,7 @@ class RequestMethod < BaseRequestTest # Reset original acronym set ActiveSupport::Inflector.inflections do |inflect| inflect.send(:instance_variable_set, "@acronyms", existing_acronyms) - inflect.send(:instance_variable_set, "@acronym_regex", existing_acronym_regex) + inflect.send(:define_acronym_regex_patterns) end end end diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index a639eddf10..6cfa8bb5aa 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -66,17 +66,21 @@ module ActiveSupport @__instance__[locale] ||= new end - attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex + attr_reader :plurals, :singulars, :uncountables, :humans, + :acronyms, :acronym_regex + attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc: def initialize - @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], Uncountables.new, [], {}, /(?=a)b/ + @plurals, @singulars, @uncountables, @humans, @acronyms = [], [], Uncountables.new, [], {} + define_acronym_regex_patterns end # Private, for the test suite. def initialize_dup(orig) # :nodoc: - %w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope| + %w(plurals singulars uncountables humans acronyms).each do |scope| instance_variable_set("@#{scope}", orig.send(scope).dup) end + define_acronym_regex_patterns end # Specifies a new acronym. An acronym must be specified as it will appear @@ -130,7 +134,7 @@ module ActiveSupport # camelize 'mcdonald' # => 'McDonald' def acronym(word) @acronyms[word.downcase] = word - @acronym_regex = /#{@acronyms.values.join("|")}/ + define_acronym_regex_patterns end # Specifies a new pluralization rule and its replacement. The rule can @@ -225,6 +229,14 @@ module ActiveSupport instance_variable_set "@#{scope}", [] end end + + private + + def define_acronym_regex_patterns + @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/ + @acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/ + @acronyms_underscore_regex = /(?:(?<=([A-Za-z\d]))|\b)(#{@acronym_regex})(?=\b|[^a-z])/ + end end # Yields a singleton instance of Inflector::Inflections so you can specify diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 72de42b1c3..43f21c03c7 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -71,7 +71,7 @@ module ActiveSupport if uppercase_first_letter string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize } else - string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase } + string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase } end string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" } string.gsub!("/".freeze, "::".freeze) @@ -92,7 +92,7 @@ module ActiveSupport def underscore(camel_cased_word) return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word) word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze) - word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" } + word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" } word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze) word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze) word.tr!("-".freeze, "_".freeze) -- cgit v1.2.3 From 363eec42e99716c896b1797f1fedc03bc30b841b Mon Sep 17 00:00:00 2001 From: Renan Gurgel Date: Mon, 23 Oct 2017 21:21:03 -0300 Subject: Space between { and | missing. Improve a code style recommendation following Rubocop instructions https://codeclimate.com/github/rails/rails/issues --- railties/lib/rails/generators/rails/plugin/plugin_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index 331e73d1f0..65a124b0b8 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -92,7 +92,7 @@ task default: :test ] def generate_test_dummy(force = false) - opts = (options.dup || {}).keep_if {|k, | PASSTHROUGH_OPTIONS.map(&:to_s).include?(k) } + opts = (options.dup || {}).keep_if { |k, | PASSTHROUGH_OPTIONS.map(&:to_s).include?(k) } opts[:force] = force opts[:skip_bundle] = true opts[:skip_listen] = true -- cgit v1.2.3 From 019583e939f6615590a4d6c1e4370e4e973fedff Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Tue, 24 Oct 2017 21:47:11 +0900 Subject: Ensure associations doesn't table name collide with aliased joins Currently alias tracker only refer a table name, doesn't respect an alias name. Should use `join.left.name` rather than `join.left.table_name`. --- activerecord/lib/active_record/associations/alias_tracker.rb | 2 +- .../test/cases/associations/inner_join_association_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index 6bac5fd3f5..07bd0a273b 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -31,7 +31,7 @@ module ActiveRecord /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i ).size elsif join.respond_to? :left - join.left.table_name == name ? 1 : 0 + join.left.name == name ? 1 : 0 elsif join.is_a?(Hash) join[name] else diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index eb85acf37e..7be875fec6 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -37,6 +37,14 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase assert_match(/agents_people_2/i, sql) end + def test_construct_finder_sql_does_not_table_name_collide_with_aliased_joins + people = Person.arel_table + agents = people.alias("agents_people") + constraint = agents[:primary_contact_id].eq(people[:id]) + sql = Person.joins(:agents).joins(agents.create_join(agents, agents.create_on(constraint))).to_sql + assert_match(/agents_people_2/i, sql) + end + def test_construct_finder_sql_ignores_empty_joins_hash sql = Author.joins({}).to_sql assert_no_match(/JOIN/i, sql) -- cgit v1.2.3 From a4eed027411c7db1091d2fdc26135b67c9b985bd Mon Sep 17 00:00:00 2001 From: Renan Gurgel Date: Tue, 24 Oct 2017 10:19:29 -0300 Subject: add _ for unused last arg --- railties/lib/rails/generators/rails/plugin/plugin_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index 65a124b0b8..d9ca4712d0 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -92,7 +92,7 @@ task default: :test ] def generate_test_dummy(force = false) - opts = (options.dup || {}).keep_if { |k, | PASSTHROUGH_OPTIONS.map(&:to_s).include?(k) } + opts = (options.dup || {}).keep_if { |k, _| PASSTHROUGH_OPTIONS.map(&:to_s).include?(k) } opts[:force] = force opts[:skip_bundle] = true opts[:skip_listen] = true -- cgit v1.2.3 From d86370252ea33c5b6fba2d53efa15b1d0082a487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Fernandes?= Date: Tue, 24 Oct 2017 15:13:34 +0100 Subject: Make clear that Time core extensions are split between Numeric and Integer The documentation wrongly suggests that Time extensions to Numeric include methods months and years, when these belong to Integer. Update both classes and guides. --- .../lib/active_support/core_ext/integer/time.rb | 2 ++ .../lib/active_support/core_ext/numeric/time.rb | 14 +++++---- guides/source/active_support_core_extensions.md | 34 ++++++++++++++++++---- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/integer/time.rb b/activesupport/lib/active_support/core_ext/integer/time.rb index 74c73ab064..66160b3dd7 100644 --- a/activesupport/lib/active_support/core_ext/integer/time.rb +++ b/activesupport/lib/active_support/core_ext/integer/time.rb @@ -19,6 +19,8 @@ class Integer # # # equivalent to Time.now.advance(months: 4, years: 5) # (4.months + 5.years).from_now + # + # For other durations, check the extensions to Numeric. def months ActiveSupport::Duration.months(self) end diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb index d62b57fe27..dee9141356 100644 --- a/activesupport/lib/active_support/core_ext/numeric/time.rb +++ b/activesupport/lib/active_support/core_ext/numeric/time.rb @@ -7,19 +7,21 @@ require "active_support/core_ext/date/calculations" require "active_support/core_ext/date/acts_like" class Numeric - # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. + # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.weeks. # # These methods use Time#advance for precise date calculations when using from_now, ago, etc. # as well as adding or subtracting their results from a Time object. For example: # - # # equivalent to Time.current.advance(months: 1) + # # equivalent to Time.current.advance(days: 1) # 1.month.from_now # - # # equivalent to Time.current.advance(years: 2) - # 2.years.from_now + # # equivalent to Time.current.advance(weeks: 2) + # 2.weeks.from_now # - # # equivalent to Time.current.advance(months: 4, years: 5) - # (4.months + 5.years).from_now + # # equivalent to Time.current.advance(days: 4, weeks: 5) + # (4.days + 5.weeks).from_now + # + # For other durations, check the extensions to Integer. def seconds ActiveSupport::Duration.seconds(self) end diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 067a7b7cb6..54d3dec1c2 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -1796,7 +1796,7 @@ NOTE: Defined in `active_support/core_ext/numeric/bytes.rb`. ### Time -Enables the use of time calculations and declarations, like `45.minutes + 2.hours + 4.years`. +Enables the use of time calculations and declarations, like `45.minutes + 2.hours + 4.weeks`. These methods use Time#advance for precise date calculations when using from_now, ago, etc. as well as adding or subtracting their results from a Time object. For example: @@ -1805,13 +1805,15 @@ as well as adding or subtracting their results from a Time object. For example: # equivalent to Time.current.advance(months: 1) 1.month.from_now -# equivalent to Time.current.advance(years: 2) -2.years.from_now +# equivalent to Time.current.advance(weeks: 2) +2.weeks.from_now -# equivalent to Time.current.advance(months: 4, years: 5) -(4.months + 5.years).from_now +# equivalent to Time.current.advance(months: 4, weeks: 5) +(4.months + 5.weeks).from_now ``` +WARNING. For other durations please refer to the time extensions to `Integer`. + NOTE: Defined in `active_support/core_ext/numeric/time.rb`. ### Formatting @@ -1947,6 +1949,28 @@ The method `ordinalize` returns the ordinal string corresponding to the receiver NOTE: Defined in `active_support/core_ext/integer/inflections.rb`. +### Time + +Enables the use of time calculations and declarations, like `4.months + 5.years`. + +These methods use Time#advance for precise date calculations when using from_now, ago, etc. +as well as adding or subtracting their results from a Time object. For example: + +```ruby +# equivalent to Time.current.advance(months: 1) +1.month.from_now + +# equivalent to Time.current.advance(years: 2) +2.years.from_now + +# equivalent to Time.current.advance(months: 4, years: 5) +(4.months + 5.years).from_now +``` + +WARNING. For other durations please refer to the time extensions to `Numeric`. + +NOTE: Defined in `active_support/core_ext/integer/time.rb`. + Extensions to `BigDecimal` -------------------------- ### `to_s` -- cgit v1.2.3 From 82ae8369925e152d507486f7520558ac09f090e8 Mon Sep 17 00:00:00 2001 From: Paul Kuruvilla Date: Tue, 24 Oct 2017 19:03:37 +0530 Subject: Save index order :desc to schema.rb (sqlite). Fixes #30902 Although the sqlite adapter supports index sort orders, they weren't being written to db/schema.rb. --- activerecord/CHANGELOG.md | 7 +++++++ .../connection_adapters/sqlite3/schema_statements.rb | 11 +++++++++-- activerecord/test/cases/schema_dumper_test.rb | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ee73004810..3e1b467633 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Fixed a bug where column orders for an index weren't written to + db/schema.rb when using the sqlite adapter. + + Fixes #30902. + + *Paul Kuruvilla* + * Remove deprecated method `#sanitize_conditions`. *Rafael Mendonça França* diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb index f0d702136d..3ab9dee370 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb @@ -19,8 +19,14 @@ module ActiveRecord /\sWHERE\s+(?.+)$/i =~ index_sql - columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col| - col["name"] + columns = [] + orders = {} + exec_query("PRAGMA index_xinfo(#{quote(row['name'])})", "SCHEMA").each do |col| + # xinfo also lists non-key columns, let's filter those out + next if col["key"] == 0 + + columns << col["name"] + orders[col["name"]] = :desc if col["desc"] == 1 end IndexDefinition.new( @@ -28,6 +34,7 @@ module ActiveRecord row["name"], row["unique"] != 0, columns, + orders: orders, where: where ) end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 799a65c61e..b8696b33e4 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -186,7 +186,7 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }', index_definition end else - assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition + assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition end end -- cgit v1.2.3 From 61ac2167eff741bffb44aec231f4ea13d004134e Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sat, 16 Sep 2017 17:33:53 +0300 Subject: Allows pass argument for `Time#prev_day` and `Time#next_day` --- activesupport/CHANGELOG.md | 27 ++++++++++++++++++++++ .../core_ext/date_and_time/calculations.rb | 12 +++++----- .../test/core_ext/date_and_time_behavior.rb | 10 ++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 8e8e9b9440..435c863025 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,30 @@ +* Add same method signature for `Time#prev_day` and `Time#next_day` + in accordance with `Date#prev_day`, `Date#next_day`. + + Allows pass argument for `Time#prev_day` and `Time#next_day`. + + Before: + ``` + Time.new(2017, 9, 16, 17, 0).prev_day # => 2017-09-15 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).prev_day(1) + # => ArgumentError: wrong number of arguments (given 1, expected 0) + + Time.new(2017, 9, 16, 17, 0).next_day # => 2017-09-17 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).next_day(1) + # => ArgumentError: wrong number of arguments (given 1, expected 0) + ``` + + After: + ``` + Time.new(2017, 9, 16, 17, 0).prev_day # => 2017-09-15 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).prev_day(1) # => 2017-09-15 17:00:00 +0300 + + Time.new(2017, 9, 16, 17, 0).next_day # => 2017-09-17 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).next_day(1) # => 2017-09-17 17:00:00 +0300 + ``` + + *bogdanvlviv* + * `IO#to_json` now returns the `to_s` representation, rather than attempting to convert to an array. This fixes a bug where `IO#to_json` would raise an `IOError` when called on an unreadable object. diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index 265c6949e1..9b5477b199 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -20,9 +20,9 @@ module DateAndTime advance(days: -1) end - # Returns a new date/time representing the previous day. - def prev_day - advance(days: -1) + # Returns a new date/time the specified number of days ago. + def prev_day(days = 1) + advance(days: -days) end # Returns a new date/time representing tomorrow. @@ -30,9 +30,9 @@ module DateAndTime advance(days: 1) end - # Returns a new date/time representing the next day. - def next_day - advance(days: 1) + # Returns a new date/time the specified number of days in the future. + def next_day(days = 1) + advance(days: days) end # Returns true if the date/time is today. diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb index 256353c309..438eb8b755 100644 --- a/activesupport/test/core_ext/date_and_time_behavior.rb +++ b/activesupport/test/core_ext/date_and_time_behavior.rb @@ -9,6 +9,11 @@ module DateAndTimeBehavior end def test_prev_day + assert_equal date_time_init(2005, 2, 24, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(-2) + assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(-1) + assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(0) + assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(1) + assert_equal date_time_init(2005, 2, 20, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(2) assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day assert_equal date_time_init(2005, 2, 28, 10, 10, 10), date_time_init(2005, 3, 2, 10, 10, 10).prev_day.prev_day end @@ -19,6 +24,11 @@ module DateAndTimeBehavior end def test_next_day + assert_equal date_time_init(2005, 2, 20, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(-2) + assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(-1) + assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(0) + assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(1) + assert_equal date_time_init(2005, 2, 24, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(2) assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day assert_equal date_time_init(2005, 3, 2, 10, 10, 10), date_time_init(2005, 2, 28, 10, 10, 10).next_day.next_day end -- cgit v1.2.3 From f2c1e3a793570584d9708aaee387214bc3543530 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sat, 16 Sep 2017 18:36:49 +0300 Subject: Allows pass argument for `Time#prev_month` and `Time#next_month` --- activesupport/CHANGELOG.md | 27 ++++++++++++++++++++++ .../core_ext/date_and_time/calculations.rb | 14 +++++++---- .../test/core_ext/date_and_time_behavior.rb | 24 +++++++++++++++++++ activesupport/test/core_ext/date_ext_test.rb | 4 ---- activesupport/test/core_ext/date_time_ext_test.rb | 4 ---- activesupport/test/core_ext/time_ext_test.rb | 4 ---- guides/source/active_support_core_extensions.md | 12 ++++++---- 7 files changed, 67 insertions(+), 22 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 435c863025..14435649cd 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,30 @@ +* Add same method signature for `Time#prev_month` and `Time#next_month` + in accordance with `Date#prev_month`, `Date#next_month`. + + Allows pass argument for `Time#prev_month` and `Time#next_month`. + + Before: + ``` + Time.new(2017, 9, 16, 17, 0).prev_month # => 2017-08-16 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).prev_month(1) + # => ArgumentError: wrong number of arguments (given 1, expected 0) + + Time.new(2017, 9, 16, 17, 0).next_month # => 2017-10-16 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).next_month(1) + # => ArgumentError: wrong number of arguments (given 1, expected 0) + ``` + + After: + ``` + Time.new(2017, 9, 16, 17, 0).prev_month # => 2017-08-16 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).prev_month(1) # => 2017-08-16 17:00:00 +0300 + + Time.new(2017, 9, 16, 17, 0).next_month # => 2017-10-16 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).next_month(1) # => 2017-10-16 17:00:00 +0300 + ``` + + *bogdanvlviv* + * Add same method signature for `Time#prev_day` and `Time#next_day` in accordance with `Date#prev_day`, `Date#next_day`. diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index 9b5477b199..59c87b9c06 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -188,9 +188,9 @@ module DateAndTime end end - # Short-hand for months_since(1). - def next_month - months_since(1) + # Returns a new date/time the specified number of months in the future. + def next_month(months = 1) + advance(months: months) end # Short-hand for months_since(3) @@ -223,11 +223,15 @@ module DateAndTime end alias_method :last_weekday, :prev_weekday + # Returns a new date/time the specified number of months ago. + def prev_month(months = 1) + advance(months: -months) + end + # Short-hand for months_ago(1). - def prev_month + def last_month months_ago(1) end - alias_method :last_month, :prev_month # Short-hand for months_ago(3). def prev_quarter diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb index 438eb8b755..091d5611d0 100644 --- a/activesupport/test/core_ext/date_and_time_behavior.rb +++ b/activesupport/test/core_ext/date_and_time_behavior.rb @@ -161,6 +161,16 @@ module DateAndTimeBehavior assert_equal date_time_init(2015, 1, 5, 15, 15, 10), date_time_init(2015, 1, 3, 15, 15, 10).next_weekday end + def test_next_month + assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(-2) + assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(-1) + assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(0) + assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(1) + assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(2) + assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month + assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month.next_month + end + def test_next_month_on_31st assert_equal date_time_init(2005, 9, 30, 15, 15, 10), date_time_init(2005, 8, 31, 15, 15, 10).next_month end @@ -213,6 +223,16 @@ module DateAndTimeBehavior assert_equal date_time_init(2015, 1, 2, 15, 15, 10), date_time_init(2015, 1, 4, 15, 15, 10).prev_weekday end + def test_prev_month + assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(-2) + assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(-1) + assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(0) + assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(1) + assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(2) + assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month + assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month.prev_month + end + def test_prev_month_on_31st assert_equal date_time_init(2004, 2, 29, 10, 10, 10), date_time_init(2004, 3, 31, 10, 10, 10).prev_month end @@ -225,6 +245,10 @@ module DateAndTimeBehavior assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year end + def test_last_month_on_31st + assert_equal date_time_init(2004, 2, 29, 0, 0, 0), date_time_init(2004, 3, 31, 0, 0, 0).last_month + end + def test_days_to_week_start assert_equal 0, date_time_init(2011, 11, 01, 0, 0, 0).days_to_week_start(:tuesday) assert_equal 1, date_time_init(2011, 11, 02, 0, 0, 0).days_to_week_start(:tuesday) diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index b8672eac4b..1d05ac6157 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -185,10 +185,6 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Date.new(1582, 10, 18), Date.new(1582, 10, 4).next_week end - def test_last_month_on_31st - assert_equal Date.new(2004, 2, 29), Date.new(2004, 3, 31).last_month - end - def test_last_quarter_on_31st assert_equal Date.new(2004, 2, 29), Date.new(2004, 5, 31).last_quarter end diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index a3c2018a31..a795ed0102 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -248,10 +248,6 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal DateTime.civil(2016, 2, 29), DateTime.civil(2016, 3, 7).last_week end - def test_last_month_on_31st - assert_equal DateTime.civil(2004, 2, 29), DateTime.civil(2004, 3, 31).last_month - end - def test_last_quarter_on_31st assert_equal DateTime.civil(2004, 2, 29), DateTime.civil(2004, 5, 31).last_quarter end diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index dc2f4c5ac7..b720e32022 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -664,10 +664,6 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase end end - def test_last_month_on_31st - assert_equal Time.local(2004, 2, 29), Time.local(2004, 3, 31).last_month - end - def test_xmlschema_is_available assert_nothing_raised { Time.now.xmlschema } end diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 54d3dec1c2..ae64ad93d6 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -3019,11 +3019,9 @@ Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000 Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000 ``` -`prev_month` is aliased to `last_month`. - ##### `prev_quarter`, `next_quarter` -Same as `prev_month` and `next_month`. It returns the date with the same day in the previous or next quarter: +`prev_quarter` and `next_quarter` return the date with the same day in the previous or next quarter: ```ruby t = Time.local(2010, 5, 8) # => Sat, 08 May 2010 @@ -3175,6 +3173,8 @@ Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010 Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010 ``` +`last_month` is short-hand for `#months_ago(1)`. + ##### `weeks_ago` The method `weeks_ago` works analogously for weeks: @@ -3353,8 +3353,9 @@ months_ago months_since beginning_of_month (at_beginning_of_month) end_of_month (at_end_of_month) -prev_month (last_month) +prev_month next_month +last_month beginning_of_quarter (at_beginning_of_quarter) end_of_quarter (at_end_of_quarter) beginning_of_year (at_beginning_of_year) @@ -3541,8 +3542,9 @@ months_ago months_since beginning_of_month (at_beginning_of_month) end_of_month (at_end_of_month) -prev_month (last_month) +prev_month next_month +last_month beginning_of_quarter (at_beginning_of_quarter) end_of_quarter (at_end_of_quarter) beginning_of_year (at_beginning_of_year) -- cgit v1.2.3 From ee9d81837b5eba9d5ec869ae7601d7ffce763e3e Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sat, 16 Sep 2017 19:06:45 +0300 Subject: Allows pass argument for `Time#prev_year` and `Time#next_year`. --- activesupport/CHANGELOG.md | 27 ++++++++++++++++++++++ .../core_ext/date_and_time/calculations.rb | 14 +++++++---- .../test/core_ext/date_and_time_behavior.rb | 16 +++++++++++++ activesupport/test/core_ext/date_ext_test.rb | 4 ---- activesupport/test/core_ext/date_time_ext_test.rb | 4 ---- activesupport/test/core_ext/time_ext_test.rb | 4 ---- guides/source/active_support_core_extensions.md | 10 ++++---- 7 files changed, 58 insertions(+), 21 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 14435649cd..d664bc2027 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,30 @@ +* Add same method signature for `Time#prev_year` and `Time#next_year` + in accordance with `Date#prev_year`, `Date#next_year`. + + Allows pass argument for `Time#prev_year` and `Time#next_year`. + + Before: + ``` + Time.new(2017, 9, 16, 17, 0).prev_year # => 2016-09-16 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).prev_year(1) + # => ArgumentError: wrong number of arguments (given 1, expected 0) + + Time.new(2017, 9, 16, 17, 0).next_year # => 2018-09-16 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).next_year(1) + # => ArgumentError: wrong number of arguments (given 1, expected 0) + ``` + + After: + ``` + Time.new(2017, 9, 16, 17, 0).prev_year # => 2016-09-16 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).prev_year(1) # => 2016-09-16 17:00:00 +0300 + + Time.new(2017, 9, 16, 17, 0).next_year # => 2018-09-16 17:00:00 +0300 + Time.new(2017, 9, 16, 17, 0).next_year(1) # => 2018-09-16 17:00:00 +0300 + ``` + + *bogdanvlviv* + * Add same method signature for `Time#prev_month` and `Time#next_month` in accordance with `Date#prev_month`, `Date#next_month`. diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index 59c87b9c06..061b79e098 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -198,9 +198,9 @@ module DateAndTime months_since(3) end - # Short-hand for years_since(1). - def next_year - years_since(1) + # Returns a new date/time the specified number of years in the future. + def next_year(years = 1) + advance(years: years) end # Returns a new date/time representing the given day in the previous week. @@ -239,11 +239,15 @@ module DateAndTime end alias_method :last_quarter, :prev_quarter + # Returns a new date/time the specified number of years ago. + def prev_year(years = 1) + advance(years: -years) + end + # Short-hand for years_ago(1). - def prev_year + def last_year years_ago(1) end - alias_method :last_year, :prev_year # Returns the number of days to the start of the week on the given day. # Week is assumed to start on +start_day+, default is diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb index 091d5611d0..42da6f6cd0 100644 --- a/activesupport/test/core_ext/date_and_time_behavior.rb +++ b/activesupport/test/core_ext/date_and_time_behavior.rb @@ -180,7 +180,13 @@ module DateAndTimeBehavior end def test_next_year + assert_equal date_time_init(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(-2) + assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(-1) + assert_equal date_time_init(2005, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(0) + assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(1) + assert_equal date_time_init(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(2) assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year + assert_equal date_time_init(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year.next_year end def test_prev_week @@ -242,13 +248,23 @@ module DateAndTimeBehavior end def test_prev_year + assert_equal date_time_init(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(-2) + assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(-1) + assert_equal date_time_init(2005, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(0) + assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(1) + assert_equal date_time_init(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(2) assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year + assert_equal date_time_init(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year.prev_year end def test_last_month_on_31st assert_equal date_time_init(2004, 2, 29, 0, 0, 0), date_time_init(2004, 3, 31, 0, 0, 0).last_month end + def test_last_year + assert_equal date_time_init(2004, 6, 5, 10, 0, 0), date_time_init(2005, 6, 5, 10, 0, 0).last_year + end + def test_days_to_week_start assert_equal 0, date_time_init(2011, 11, 01, 0, 0, 0).days_to_week_start(:tuesday) assert_equal 1, date_time_init(2011, 11, 02, 0, 0, 0).days_to_week_start(:tuesday) diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 1d05ac6157..0c6f3f595a 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -120,10 +120,6 @@ class DateExtCalculationsTest < ActiveSupport::TestCase assert_equal Date.new(1582, 10, 4), Date.new(1583, 10, 14).prev_year end - def test_last_year - assert_equal Date.new(2004, 6, 5), Date.new(2005, 6, 5).last_year - end - def test_last_year_in_leap_years assert_equal Date.new(1999, 2, 28), Date.new(2000, 2, 29).last_year end diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index a795ed0102..d942cddb2a 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -162,10 +162,6 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase assert_equal DateTime.civil(2005, 4, 30, 23, 59, Rational(59999999999, 1000000000)), DateTime.civil(2005, 4, 20, 10, 10, 10).end_of_month end - def test_last_year - assert_equal DateTime.civil(2004, 6, 5, 10), DateTime.civil(2005, 6, 5, 10, 0, 0).last_year - end - def test_ago assert_equal DateTime.civil(2005, 2, 22, 10, 10, 9), DateTime.civil(2005, 2, 22, 10, 10, 10).ago(1) assert_equal DateTime.civil(2005, 2, 22, 9, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).ago(3600) diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index b720e32022..8cb17df01b 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -178,10 +178,6 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005, 2, 4, 19, 30, 59, Rational(999999999, 1000)), Time.local(2005, 2, 4, 19, 30, 10).end_of_minute end - def test_last_year - assert_equal Time.local(2004, 6, 5, 10), Time.local(2005, 6, 5, 10, 0, 0).last_year - end - def test_ago assert_equal Time.local(2005, 2, 22, 10, 10, 9), Time.local(2005, 2, 22, 10, 10, 10).ago(1) assert_equal Time.local(2005, 2, 22, 9, 10, 10), Time.local(2005, 2, 22, 10, 10, 10).ago(3600) diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index ae64ad93d6..a80fd5dcc1 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -2998,8 +2998,6 @@ d.prev_year # => Sun, 28 Feb 1999 d.next_year # => Wed, 28 Feb 2001 ``` -`prev_year` is aliased to `last_year`. - ##### `prev_month`, `next_month` In Ruby 1.9 `prev_month` and `next_month` return the date with the same day in the last or next month: @@ -3157,6 +3155,8 @@ Date.new(2012, 2, 29).years_ago(3) # => Sat, 28 Feb 2009 Date.new(2012, 2, 29).years_since(3) # => Sat, 28 Feb 2015 ``` +`last_year` is short-hand for `#years_ago(1)`. + ##### `months_ago`, `months_since` The methods `months_ago` and `months_since` work analogously for months: @@ -3362,7 +3362,8 @@ beginning_of_year (at_beginning_of_year) end_of_year (at_end_of_year) years_ago years_since -prev_year (last_year) +prev_year +last_year next_year on_weekday? on_weekend? @@ -3551,7 +3552,8 @@ beginning_of_year (at_beginning_of_year) end_of_year (at_end_of_year) years_ago years_since -prev_year (last_year) +prev_year +last_year next_year on_weekday? on_weekend? -- cgit v1.2.3 From 97ee3dc788b0f596e182313dd32cb876e01fc2e9 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sat, 16 Sep 2017 21:24:53 +0300 Subject: Update "Active Support Core Extensions" guide --- guides/source/active_support_core_extensions.md | 188 ++++++++++++------------ 1 file changed, 97 insertions(+), 91 deletions(-) diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index a80fd5dcc1..66d2fbd939 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -2970,6 +2970,32 @@ Extensions to `Date` NOTE: All the following methods are defined in `active_support/core_ext/date/calculations.rb`. +```ruby +yesterday +tomorrow +beginning_of_week (at_beginning_of_week) +end_of_week (at_end_of_week) +monday +sunday +weeks_ago +prev_week (last_week) +next_week +months_ago +months_since +beginning_of_month (at_beginning_of_month) +end_of_month (at_end_of_month) +last_month +beginning_of_quarter (at_beginning_of_quarter) +end_of_quarter (at_end_of_quarter) +beginning_of_year (at_beginning_of_year) +end_of_year (at_end_of_year) +years_ago +years_since +last_year +on_weekday? +on_weekend? +``` + INFO: The following calculation methods have edge cases in October 1582, since days 5..14 just do not exist. This guide does not document their behavior around those days for brevity, but it is enough to say that they do what you would expect. That is, `Date.new(1582, 10, 4).tomorrow` returns `Date.new(1582, 10, 15)` and so on. Please check `test/core_ext/date_ext_test.rb` in the Active Support test suite for expected behavior. #### `Date.current` @@ -2980,64 +3006,6 @@ When making Date comparisons using methods which honor the user time zone, make #### Named dates -##### `prev_year`, `next_year` - -In Ruby 1.9 `prev_year` and `next_year` return a date with the same day/month in the last or next year: - -```ruby -d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 -d.prev_year # => Fri, 08 May 2009 -d.next_year # => Sun, 08 May 2011 -``` - -If date is the 29th of February of a leap year, you obtain the 28th: - -```ruby -d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000 -d.prev_year # => Sun, 28 Feb 1999 -d.next_year # => Wed, 28 Feb 2001 -``` - -##### `prev_month`, `next_month` - -In Ruby 1.9 `prev_month` and `next_month` return the date with the same day in the last or next month: - -```ruby -d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 -d.prev_month # => Thu, 08 Apr 2010 -d.next_month # => Tue, 08 Jun 2010 -``` - -If such a day does not exist, the last day of the corresponding month is returned: - -```ruby -Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000 -Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000 -Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000 -Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000 -``` - -##### `prev_quarter`, `next_quarter` - -`prev_quarter` and `next_quarter` return the date with the same day in the previous or next quarter: - -```ruby -t = Time.local(2010, 5, 8) # => Sat, 08 May 2010 -t.prev_quarter # => Mon, 08 Feb 2010 -t.next_quarter # => Sun, 08 Aug 2010 -``` - -If such a day does not exist, the last day of the corresponding month is returned: - -```ruby -Time.local(2000, 7, 31).prev_quarter # => Sun, 30 Apr 2000 -Time.local(2000, 5, 31).prev_quarter # => Tue, 29 Feb 2000 -Time.local(2000, 10, 31).prev_quarter # => Mon, 30 Oct 2000 -Time.local(2000, 11, 31).next_quarter # => Wed, 28 Feb 2001 -``` - -`prev_quarter` is aliased to `last_quarter`. - ##### `beginning_of_week`, `end_of_week` The methods `beginning_of_week` and `end_of_week` return the dates for the @@ -3337,37 +3305,7 @@ WARNING: `DateTime` is not aware of DST rules and so some of these methods have NOTE: All the following methods are defined in `active_support/core_ext/date_time/calculations.rb`. -The class `DateTime` is a subclass of `Date` so by loading `active_support/core_ext/date/calculations.rb` you inherit these methods and their aliases, except that they will always return datetimes: - -```ruby -yesterday -tomorrow -beginning_of_week (at_beginning_of_week) -end_of_week (at_end_of_week) -monday -sunday -weeks_ago -prev_week (last_week) -next_week -months_ago -months_since -beginning_of_month (at_beginning_of_month) -end_of_month (at_end_of_month) -prev_month -next_month -last_month -beginning_of_quarter (at_beginning_of_quarter) -end_of_quarter (at_end_of_quarter) -beginning_of_year (at_beginning_of_year) -end_of_year (at_end_of_year) -years_ago -years_since -prev_year -last_year -next_year -on_weekday? -on_weekend? -``` +The class `DateTime` is a subclass of `Date` so by loading `active_support/core_ext/date/calculations.rb` you inherit these methods and their aliases, except that they will always return datetimes. The following methods are reimplemented so you do **not** need to load `active_support/core_ext/date/calculations.rb` for these ones: @@ -3515,8 +3453,6 @@ Extensions to `Time` NOTE: All the following methods are defined in `active_support/core_ext/time/calculations.rb`. -Active Support adds to `Time` many of the methods available for `DateTime`: - ```ruby past? today? @@ -3528,6 +3464,8 @@ change advance ago since (in) +prev_day +next_day beginning_of_day (midnight, at_midnight, at_beginning_of_day) end_of_day beginning_of_hour (at_beginning_of_hour) @@ -3611,6 +3549,74 @@ now.all_year # => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00 ``` +#### `prev_day`, `next_day` + +In Ruby 1.9 `prev_day` and `next_day` return the date in the last or next day: + +```ruby +d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 +d.prev_day # => Fri, 07 May 2010 +d.next_day # => Sun, 09 May 2010 +``` + +#### `prev_month`, `next_month` + +In Ruby 1.9 `prev_month` and `next_month` return the date with the same day in the last or next month: + +```ruby +d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 +d.prev_month # => Thu, 08 Apr 2010 +d.next_month # => Tue, 08 Jun 2010 +``` + +If such a day does not exist, the last day of the corresponding month is returned: + +```ruby +Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000 +Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000 +Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000 +Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000 +``` + +#### `prev_year`, `next_year` + +In Ruby 1.9 `prev_year` and `next_year` return a date with the same day/month in the last or next year: + +```ruby +d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 +d.prev_year # => Fri, 08 May 2009 +d.next_year # => Sun, 08 May 2011 +``` + +If date is the 29th of February of a leap year, you obtain the 28th: + +```ruby +d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000 +d.prev_year # => Sun, 28 Feb 1999 +d.next_year # => Wed, 28 Feb 2001 +``` + +#### `prev_quarter`, `next_quarter` + +`prev_quarter` and `next_quarter` return the date with the same day in the previous or next quarter: + +```ruby +t = Time.local(2010, 5, 8) # => 2010-05-08 00:00:00 +0300 +t.prev_quarter # => 2010-02-08 00:00:00 +0200 +t.next_quarter # => 2010-08-08 00:00:00 +0300 +``` + +If such a day does not exist, the last day of the corresponding month is returned: + +```ruby +Time.local(2000, 7, 31).prev_quarter # => 2000-04-30 00:00:00 +0300 +Time.local(2000, 5, 31).prev_quarter # => 2000-02-29 00:00:00 +0200 +Time.local(2000, 10, 31).prev_quarter # => 2000-07-31 00:00:00 +0300 +Time.local(2000, 11, 31).next_quarter # => 2001-03-01 00:00:00 +0200 +``` + +`prev_quarter` is aliased to `last_quarter`. + ### Time Constructors Active Support defines `Time.current` to be `Time.zone.now` if there's a user time zone defined, with fallback to `Time.now`: -- cgit v1.2.3 From 08a177ee63c74fde62426c6af5fda18488747de9 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Tue, 24 Oct 2017 21:03:35 +0000 Subject: Simplify API documentation of methods that return a Duration Related to #30972 --- .../lib/active_support/core_ext/integer/time.rb | 21 +++++---------------- .../lib/active_support/core_ext/numeric/time.rb | 22 +++++----------------- 2 files changed, 10 insertions(+), 33 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/integer/time.rb b/activesupport/lib/active_support/core_ext/integer/time.rb index 66160b3dd7..5efb89cf9f 100644 --- a/activesupport/lib/active_support/core_ext/integer/time.rb +++ b/activesupport/lib/active_support/core_ext/integer/time.rb @@ -4,28 +4,17 @@ require "active_support/duration" require "active_support/core_ext/numeric/time" class Integer - # Enables the use of time calculations and declarations, like 45.minutes + - # 2.hours + 4.years. + # Returns a Duration instance matching the number of months provided. # - # These methods use Time#advance for precise date calculations when using - # from_now, +ago+, etc. as well as adding or subtracting their - # results from a Time object. - # - # # equivalent to Time.now.advance(months: 1) - # 1.month.from_now - # - # # equivalent to Time.now.advance(years: 2) - # 2.years.from_now - # - # # equivalent to Time.now.advance(months: 4, years: 5) - # (4.months + 5.years).from_now - # - # For other durations, check the extensions to Numeric. + # 2.months # => 2 months def months ActiveSupport::Duration.months(self) end alias :month :months + # Returns a Duration instance matching the number of years provided. + # + # 2.years # => 2 years def years ActiveSupport::Duration.years(self) end diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb index dee9141356..bc4627f7a2 100644 --- a/activesupport/lib/active_support/core_ext/numeric/time.rb +++ b/activesupport/lib/active_support/core_ext/numeric/time.rb @@ -7,21 +7,9 @@ require "active_support/core_ext/date/calculations" require "active_support/core_ext/date/acts_like" class Numeric - # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.weeks. + # Returns a Duration instance matching the number of seconds provided. # - # These methods use Time#advance for precise date calculations when using from_now, ago, etc. - # as well as adding or subtracting their results from a Time object. For example: - # - # # equivalent to Time.current.advance(days: 1) - # 1.month.from_now - # - # # equivalent to Time.current.advance(weeks: 2) - # 2.weeks.from_now - # - # # equivalent to Time.current.advance(days: 4, weeks: 5) - # (4.days + 5.weeks).from_now - # - # For other durations, check the extensions to Integer. + # 2.seconds # => 2 seconds def seconds ActiveSupport::Duration.seconds(self) end @@ -68,10 +56,10 @@ class Numeric alias :fortnight :fortnights # Returns the number of milliseconds equivalent to the seconds provided. - # Used with the standard time durations, like 1.hour.in_milliseconds -- - # so we can feed them to JavaScript functions like getTime(). + # Used with the standard time durations. # - # 2.in_milliseconds # => 2_000 + # 2.in_milliseconds # => 2000 + # 1.hour.in_milliseconds # => 3600000 def in_milliseconds self * 1000 end -- cgit v1.2.3 From 5cd6f1792e9710979ce53943c28b2b38dae34e98 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Tue, 24 Oct 2017 21:45:05 +0000 Subject: `supports_extensions?` return always true since PostgreSQL 9.1 since the minimum version of PostgreSQL currently Rails supports is 9.1, there is no need to handle if `supports_extensions?` Refer https://www.postgresql.org/docs/9.1/static/sql-createextension.html "CREATE EXTENSION" --- .../connection_adapters/postgresql_adapter.rb | 12 +- .../test/cases/adapters/postgresql/citext_test.rb | 110 ++-- .../postgresql/extension_migration_test.rb | 4 - .../test/cases/adapters/postgresql/hstore_test.rb | 590 ++++++++++----------- .../test/cases/adapters/postgresql/uuid_test.rb | 190 ++++--- activerecord/test/cases/schema_dumper_test.rb | 48 +- 6 files changed, 466 insertions(+), 488 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e9ae861bfb..2c3c1df2a9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -337,18 +337,12 @@ module ActiveRecord end def extension_enabled?(name) - if supports_extensions? - res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA") - res.cast_values.first - end + res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA") + res.cast_values.first end def extensions - if supports_extensions? - exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values - else - super - end + exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values end # Returns the configured supported identifier length supported by PostgreSQL diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb index 050614cade..a25f102bad 100644 --- a/activerecord/test/cases/adapters/postgresql/citext_test.rb +++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb @@ -3,78 +3,76 @@ require "cases/helper" require "support/schema_dumping_helper" -if ActiveRecord::Base.connection.supports_extensions? - class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase - include SchemaDumpingHelper - class Citext < ActiveRecord::Base - self.table_name = "citexts" - end - - def setup - @connection = ActiveRecord::Base.connection +class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + class Citext < ActiveRecord::Base + self.table_name = "citexts" + end - enable_extension!("citext", @connection) + def setup + @connection = ActiveRecord::Base.connection - @connection.create_table("citexts") do |t| - t.citext "cival" - end - end + enable_extension!("citext", @connection) - teardown do - @connection.drop_table "citexts", if_exists: true - disable_extension!("citext", @connection) + @connection.create_table("citexts") do |t| + t.citext "cival" end + end - def test_citext_enabled - assert @connection.extension_enabled?("citext") - end + teardown do + @connection.drop_table "citexts", if_exists: true + disable_extension!("citext", @connection) + end - def test_column - column = Citext.columns_hash["cival"] - assert_equal :citext, column.type - assert_equal "citext", column.sql_type - assert_not column.array? + def test_citext_enabled + assert @connection.extension_enabled?("citext") + end - type = Citext.type_for_attribute("cival") - assert_not type.binary? - end + def test_column + column = Citext.columns_hash["cival"] + assert_equal :citext, column.type + assert_equal "citext", column.sql_type + assert_not column.array? - def test_change_table_supports_json - @connection.transaction do - @connection.change_table("citexts") do |t| - t.citext "username" - end - Citext.reset_column_information - column = Citext.columns_hash["username"] - assert_equal :citext, column.type + type = Citext.type_for_attribute("cival") + assert_not type.binary? + end - raise ActiveRecord::Rollback # reset the schema change + def test_change_table_supports_json + @connection.transaction do + @connection.change_table("citexts") do |t| + t.citext "username" end - ensure Citext.reset_column_information + column = Citext.columns_hash["username"] + assert_equal :citext, column.type + + raise ActiveRecord::Rollback # reset the schema change end + ensure + Citext.reset_column_information + end - def test_write - x = Citext.new(cival: "Some CI Text") - x.save! - citext = Citext.first - assert_equal "Some CI Text", citext.cival + def test_write + x = Citext.new(cival: "Some CI Text") + x.save! + citext = Citext.first + assert_equal "Some CI Text", citext.cival - citext.cival = "Some NEW CI Text" - citext.save! + citext.cival = "Some NEW CI Text" + citext.save! - assert_equal "Some NEW CI Text", citext.reload.cival - end + assert_equal "Some NEW CI Text", citext.reload.cival + end - def test_select_case_insensitive - @connection.execute "insert into citexts (cival) values('Cased Text')" - x = Citext.where(cival: "cased text").first - assert_equal "Cased Text", x.cival - end + def test_select_case_insensitive + @connection.execute "insert into citexts (cival) values('Cased Text')" + x = Citext.where(cival: "cased text").first + assert_equal "Cased Text", x.cival + end - def test_schema_dump_with_shorthand - output = dump_table_schema("citexts") - assert_match %r[t\.citext "cival"], output - end + def test_schema_dump_with_shorthand + output = dump_table_schema("citexts") + assert_match %r[t\.citext "cival"], output end end diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb index e589e3ab1b..df97ab11e7 100644 --- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb +++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb @@ -22,10 +22,6 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase @connection = ActiveRecord::Base.connection - unless @connection.supports_extensions? - return skip("no extension support") - end - @old_schema_migration_table_name = ActiveRecord::SchemaMigration.table_name @old_table_name_prefix = ActiveRecord::Base.table_name_prefix @old_table_name_suffix = ActiveRecord::Base.table_name_suffix diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 97a8a257c5..f09e34b5f2 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -3,378 +3,376 @@ require "cases/helper" require "support/schema_dumping_helper" -if ActiveRecord::Base.connection.supports_extensions? - class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase - include SchemaDumpingHelper - class Hstore < ActiveRecord::Base - self.table_name = "hstores" +class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + class Hstore < ActiveRecord::Base + self.table_name = "hstores" - store_accessor :settings, :language, :timezone - end + store_accessor :settings, :language, :timezone + end - class FakeParameters - def to_unsafe_h - { "hi" => "hi" } - end + class FakeParameters + def to_unsafe_h + { "hi" => "hi" } end + end - def setup - @connection = ActiveRecord::Base.connection + def setup + @connection = ActiveRecord::Base.connection - enable_extension!("hstore", @connection) + enable_extension!("hstore", @connection) - @connection.transaction do - @connection.create_table("hstores") do |t| - t.hstore "tags", default: "" - t.hstore "payload", array: true - t.hstore "settings" - end + @connection.transaction do + @connection.create_table("hstores") do |t| + t.hstore "tags", default: "" + t.hstore "payload", array: true + t.hstore "settings" end - Hstore.reset_column_information - @column = Hstore.columns_hash["tags"] - @type = Hstore.type_for_attribute("tags") - end - - teardown do - @connection.drop_table "hstores", if_exists: true - disable_extension!("hstore", @connection) end + Hstore.reset_column_information + @column = Hstore.columns_hash["tags"] + @type = Hstore.type_for_attribute("tags") + end - def test_hstore_included_in_extensions - assert @connection.respond_to?(:extensions), "connection should have a list of extensions" - assert_includes @connection.extensions, "hstore", "extension list should include hstore" - end + teardown do + @connection.drop_table "hstores", if_exists: true + disable_extension!("hstore", @connection) + end - def test_disable_enable_hstore - assert @connection.extension_enabled?("hstore") - @connection.disable_extension "hstore" - assert_not @connection.extension_enabled?("hstore") - @connection.enable_extension "hstore" - assert @connection.extension_enabled?("hstore") - ensure - # Restore column(s) dropped by `drop extension hstore cascade;` - load_schema - end + def test_hstore_included_in_extensions + assert @connection.respond_to?(:extensions), "connection should have a list of extensions" + assert_includes @connection.extensions, "hstore", "extension list should include hstore" + end - def test_column - assert_equal :hstore, @column.type - assert_equal "hstore", @column.sql_type - assert_not @column.array? + def test_disable_enable_hstore + assert @connection.extension_enabled?("hstore") + @connection.disable_extension "hstore" + assert_not @connection.extension_enabled?("hstore") + @connection.enable_extension "hstore" + assert @connection.extension_enabled?("hstore") + ensure + # Restore column(s) dropped by `drop extension hstore cascade;` + load_schema + end - assert_not @type.binary? - end + def test_column + assert_equal :hstore, @column.type + assert_equal "hstore", @column.sql_type + assert_not @column.array? - def test_default - @connection.add_column "hstores", "permissions", :hstore, default: '"users"=>"read", "articles"=>"write"' - Hstore.reset_column_information + assert_not @type.binary? + end - assert_equal({ "users" => "read", "articles" => "write" }, Hstore.column_defaults["permissions"]) - assert_equal({ "users" => "read", "articles" => "write" }, Hstore.new.permissions) - ensure - Hstore.reset_column_information - end + def test_default + @connection.add_column "hstores", "permissions", :hstore, default: '"users"=>"read", "articles"=>"write"' + Hstore.reset_column_information - def test_change_table_supports_hstore - @connection.transaction do - @connection.change_table("hstores") do |t| - t.hstore "users", default: "" - end - Hstore.reset_column_information - column = Hstore.columns_hash["users"] - assert_equal :hstore, column.type + assert_equal({ "users" => "read", "articles" => "write" }, Hstore.column_defaults["permissions"]) + assert_equal({ "users" => "read", "articles" => "write" }, Hstore.new.permissions) + ensure + Hstore.reset_column_information + end - raise ActiveRecord::Rollback # reset the schema change + def test_change_table_supports_hstore + @connection.transaction do + @connection.change_table("hstores") do |t| + t.hstore "users", default: "" end - ensure Hstore.reset_column_information + column = Hstore.columns_hash["users"] + assert_equal :hstore, column.type + + raise ActiveRecord::Rollback # reset the schema change end + ensure + Hstore.reset_column_information + end - def test_hstore_migration - hstore_migration = Class.new(ActiveRecord::Migration::Current) do - def change - change_table("hstores") do |t| - t.hstore :keys - end + def test_hstore_migration + hstore_migration = Class.new(ActiveRecord::Migration::Current) do + def change + change_table("hstores") do |t| + t.hstore :keys end end - - hstore_migration.new.suppress_messages do - hstore_migration.migrate(:up) - assert_includes @connection.columns(:hstores).map(&:name), "keys" - hstore_migration.migrate(:down) - assert_not_includes @connection.columns(:hstores).map(&:name), "keys" - end end - def test_cast_value_on_write - x = Hstore.new tags: { "bool" => true, "number" => 5 } - assert_equal({ "bool" => true, "number" => 5 }, x.tags_before_type_cast) - assert_equal({ "bool" => "true", "number" => "5" }, x.tags) - x.save - assert_equal({ "bool" => "true", "number" => "5" }, x.reload.tags) + hstore_migration.new.suppress_messages do + hstore_migration.migrate(:up) + assert_includes @connection.columns(:hstores).map(&:name), "keys" + hstore_migration.migrate(:down) + assert_not_includes @connection.columns(:hstores).map(&:name), "keys" end + end - def test_type_cast_hstore - assert_equal({ "1" => "2" }, @type.deserialize("\"1\"=>\"2\"")) - assert_equal({}, @type.deserialize("")) - assert_equal({ "key" => nil }, @type.deserialize("key => NULL")) - assert_equal({ "c" => "}", '"a"' => 'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b"))) - end + def test_cast_value_on_write + x = Hstore.new tags: { "bool" => true, "number" => 5 } + assert_equal({ "bool" => true, "number" => 5 }, x.tags_before_type_cast) + assert_equal({ "bool" => "true", "number" => "5" }, x.tags) + x.save + assert_equal({ "bool" => "true", "number" => "5" }, x.reload.tags) + end - def test_with_store_accessors - x = Hstore.new(language: "fr", timezone: "GMT") - assert_equal "fr", x.language - assert_equal "GMT", x.timezone + def test_type_cast_hstore + assert_equal({ "1" => "2" }, @type.deserialize("\"1\"=>\"2\"")) + assert_equal({}, @type.deserialize("")) + assert_equal({ "key" => nil }, @type.deserialize("key => NULL")) + assert_equal({ "c" => "}", '"a"' => 'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b"))) + end - x.save! - x = Hstore.first - assert_equal "fr", x.language - assert_equal "GMT", x.timezone + def test_with_store_accessors + x = Hstore.new(language: "fr", timezone: "GMT") + assert_equal "fr", x.language + assert_equal "GMT", x.timezone - x.language = "de" - x.save! + x.save! + x = Hstore.first + assert_equal "fr", x.language + assert_equal "GMT", x.timezone - x = Hstore.first - assert_equal "de", x.language - assert_equal "GMT", x.timezone - end + x.language = "de" + x.save! - def test_duplication_with_store_accessors - x = Hstore.new(language: "fr", timezone: "GMT") - assert_equal "fr", x.language - assert_equal "GMT", x.timezone + x = Hstore.first + assert_equal "de", x.language + assert_equal "GMT", x.timezone + end - y = x.dup - assert_equal "fr", y.language - assert_equal "GMT", y.timezone - end + def test_duplication_with_store_accessors + x = Hstore.new(language: "fr", timezone: "GMT") + assert_equal "fr", x.language + assert_equal "GMT", x.timezone - def test_yaml_round_trip_with_store_accessors - x = Hstore.new(language: "fr", timezone: "GMT") - assert_equal "fr", x.language - assert_equal "GMT", x.timezone + y = x.dup + assert_equal "fr", y.language + assert_equal "GMT", y.timezone + end - y = YAML.load(YAML.dump(x)) - assert_equal "fr", y.language - assert_equal "GMT", y.timezone - end + def test_yaml_round_trip_with_store_accessors + x = Hstore.new(language: "fr", timezone: "GMT") + assert_equal "fr", x.language + assert_equal "GMT", x.timezone - def test_changes_in_place - hstore = Hstore.create!(settings: { "one" => "two" }) - hstore.settings["three"] = "four" - hstore.save! - hstore.reload + y = YAML.load(YAML.dump(x)) + assert_equal "fr", y.language + assert_equal "GMT", y.timezone + end - assert_equal "four", hstore.settings["three"] - assert_not hstore.changed? - end + def test_changes_in_place + hstore = Hstore.create!(settings: { "one" => "two" }) + hstore.settings["three"] = "four" + hstore.save! + hstore.reload - def test_dirty_from_user_equal - settings = { "alongkey" => "anything", "key" => "value" } - hstore = Hstore.create!(settings: settings) + assert_equal "four", hstore.settings["three"] + assert_not hstore.changed? + end - hstore.settings = { "key" => "value", "alongkey" => "anything" } - assert_equal settings, hstore.settings - refute hstore.changed? - end + def test_dirty_from_user_equal + settings = { "alongkey" => "anything", "key" => "value" } + hstore = Hstore.create!(settings: settings) - def test_hstore_dirty_from_database_equal - settings = { "alongkey" => "anything", "key" => "value" } - hstore = Hstore.create!(settings: settings) - hstore.reload + hstore.settings = { "key" => "value", "alongkey" => "anything" } + assert_equal settings, hstore.settings + refute hstore.changed? + end - assert_equal settings, hstore.settings - hstore.settings = settings - refute hstore.changed? - end + def test_hstore_dirty_from_database_equal + settings = { "alongkey" => "anything", "key" => "value" } + hstore = Hstore.create!(settings: settings) + hstore.reload - def test_gen1 - assert_equal('" "=>""', @type.serialize(" " => "")) - end + assert_equal settings, hstore.settings + hstore.settings = settings + refute hstore.changed? + end - def test_gen2 - assert_equal('","=>""', @type.serialize("," => "")) - end + def test_gen1 + assert_equal('" "=>""', @type.serialize(" " => "")) + end - def test_gen3 - assert_equal('"="=>""', @type.serialize("=" => "")) - end + def test_gen2 + assert_equal('","=>""', @type.serialize("," => "")) + end - def test_gen4 - assert_equal('">"=>""', @type.serialize(">" => "")) - end + def test_gen3 + assert_equal('"="=>""', @type.serialize("=" => "")) + end - def test_parse1 - assert_equal({ "a" => nil, "b" => nil, "c" => "NuLl", "null" => "c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c')) - end + def test_gen4 + assert_equal('">"=>""', @type.serialize(">" => "")) + end - def test_parse2 - assert_equal({ " " => " " }, @type.deserialize("\\ =>\\ ")) - end + def test_parse1 + assert_equal({ "a" => nil, "b" => nil, "c" => "NuLl", "null" => "c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c')) + end - def test_parse3 - assert_equal({ "=" => ">" }, @type.deserialize("==>>")) - end + def test_parse2 + assert_equal({ " " => " " }, @type.deserialize("\\ =>\\ ")) + end - def test_parse4 - assert_equal({ "=a" => "q=w" }, @type.deserialize('\=a=>q=w')) - end + def test_parse3 + assert_equal({ "=" => ">" }, @type.deserialize("==>>")) + end - def test_parse5 - assert_equal({ "=a" => "q=w" }, @type.deserialize('"=a"=>q\=w')) - end + def test_parse4 + assert_equal({ "=a" => "q=w" }, @type.deserialize('\=a=>q=w')) + end - def test_parse6 - assert_equal({ "\"a" => "q>w" }, @type.deserialize('"\"a"=>q>w')) - end + def test_parse5 + assert_equal({ "=a" => "q=w" }, @type.deserialize('"=a"=>q\=w')) + end - def test_parse7 - assert_equal({ "\"a" => "q\"w" }, @type.deserialize('\"a=>q"w')) - end + def test_parse6 + assert_equal({ "\"a" => "q>w" }, @type.deserialize('"\"a"=>q>w')) + end - def test_rewrite - @connection.execute "insert into hstores (tags) VALUES ('1=>2')" - x = Hstore.first - x.tags = { '"a\'' => "b" } - assert x.save! - end + def test_parse7 + assert_equal({ "\"a" => "q\"w" }, @type.deserialize('\"a=>q"w')) + end - def test_select - @connection.execute "insert into hstores (tags) VALUES ('1=>2')" - x = Hstore.first - assert_equal({ "1" => "2" }, x.tags) - end + def test_rewrite + @connection.execute "insert into hstores (tags) VALUES ('1=>2')" + x = Hstore.first + x.tags = { '"a\'' => "b" } + assert x.save! + end - def test_array_cycle - assert_array_cycle([{ "AA" => "BB", "CC" => "DD" }, { "AA" => nil }]) - end + def test_select + @connection.execute "insert into hstores (tags) VALUES ('1=>2')" + x = Hstore.first + assert_equal({ "1" => "2" }, x.tags) + end - def test_array_strings_with_quotes - assert_array_cycle([{ "this has" => 'some "s that need to be escaped"' }]) - end + def test_array_cycle + assert_array_cycle([{ "AA" => "BB", "CC" => "DD" }, { "AA" => nil }]) + end - def test_array_strings_with_commas - assert_array_cycle([{ "this,has" => "many,values" }]) - end + def test_array_strings_with_quotes + assert_array_cycle([{ "this has" => 'some "s that need to be escaped"' }]) + end - def test_array_strings_with_array_delimiters - assert_array_cycle(["{" => "}"]) - end + def test_array_strings_with_commas + assert_array_cycle([{ "this,has" => "many,values" }]) + end - def test_array_strings_with_null_strings - assert_array_cycle([{ "NULL" => "NULL" }]) - end + def test_array_strings_with_array_delimiters + assert_array_cycle(["{" => "}"]) + end - def test_contains_nils - assert_array_cycle([{ "NULL" => nil }]) - end + def test_array_strings_with_null_strings + assert_array_cycle([{ "NULL" => "NULL" }]) + end - def test_select_multikey - @connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')" - x = Hstore.first - assert_equal({ "1" => "2", "2" => "3" }, x.tags) - end + def test_contains_nils + assert_array_cycle([{ "NULL" => nil }]) + end - def test_create - assert_cycle("a" => "b", "1" => "2") - end + def test_select_multikey + @connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')" + x = Hstore.first + assert_equal({ "1" => "2", "2" => "3" }, x.tags) + end - def test_nil - assert_cycle("a" => nil) - end + def test_create + assert_cycle("a" => "b", "1" => "2") + end - def test_quotes - assert_cycle("a" => 'b"ar', '1"foo' => "2") - end + def test_nil + assert_cycle("a" => nil) + end - def test_whitespace - assert_cycle("a b" => "b ar", '1"foo' => "2") - end + def test_quotes + assert_cycle("a" => 'b"ar', '1"foo' => "2") + end - def test_backslash - assert_cycle('a\\b' => 'b\\ar', '1"foo' => "2") - end + def test_whitespace + assert_cycle("a b" => "b ar", '1"foo' => "2") + end - def test_comma - assert_cycle("a, b" => "bar", '1"foo' => "2") - end + def test_backslash + assert_cycle('a\\b' => 'b\\ar', '1"foo' => "2") + end - def test_arrow - assert_cycle("a=>b" => "bar", '1"foo' => "2") - end + def test_comma + assert_cycle("a, b" => "bar", '1"foo' => "2") + end - def test_quoting_special_characters - assert_cycle("ca" => "cà", "ac" => "àc") - end + def test_arrow + assert_cycle("a=>b" => "bar", '1"foo' => "2") + end - def test_multiline - assert_cycle("a\nb" => "c\nd") - end + def test_quoting_special_characters + assert_cycle("ca" => "cà", "ac" => "àc") + end - class TagCollection - def initialize(hash); @hash = hash end - def to_hash; @hash end - def self.load(hash); new(hash) end - def self.dump(object); object.to_hash end - end + def test_multiline + assert_cycle("a\nb" => "c\nd") + end - class HstoreWithSerialize < Hstore - serialize :tags, TagCollection - end + class TagCollection + def initialize(hash); @hash = hash end + def to_hash; @hash end + def self.load(hash); new(hash) end + def self.dump(object); object.to_hash end + end - def test_hstore_with_serialized_attributes - HstoreWithSerialize.create! tags: TagCollection.new("one" => "two") - record = HstoreWithSerialize.first - assert_instance_of TagCollection, record.tags - assert_equal({ "one" => "two" }, record.tags.to_hash) - record.tags = TagCollection.new("three" => "four") - record.save! - assert_equal({ "three" => "four" }, HstoreWithSerialize.first.tags.to_hash) - end + class HstoreWithSerialize < Hstore + serialize :tags, TagCollection + end - def test_clone_hstore_with_serialized_attributes - HstoreWithSerialize.create! tags: TagCollection.new("one" => "two") - record = HstoreWithSerialize.first - dupe = record.dup - assert_equal({ "one" => "two" }, dupe.tags.to_hash) - end + def test_hstore_with_serialized_attributes + HstoreWithSerialize.create! tags: TagCollection.new("one" => "two") + record = HstoreWithSerialize.first + assert_instance_of TagCollection, record.tags + assert_equal({ "one" => "two" }, record.tags.to_hash) + record.tags = TagCollection.new("three" => "four") + record.save! + assert_equal({ "three" => "four" }, HstoreWithSerialize.first.tags.to_hash) + end - def test_schema_dump_with_shorthand - output = dump_table_schema("hstores") - assert_match %r[t\.hstore "tags",\s+default: {}], output - end + def test_clone_hstore_with_serialized_attributes + HstoreWithSerialize.create! tags: TagCollection.new("one" => "two") + record = HstoreWithSerialize.first + dupe = record.dup + assert_equal({ "one" => "two" }, dupe.tags.to_hash) + end + + def test_schema_dump_with_shorthand + output = dump_table_schema("hstores") + assert_match %r[t\.hstore "tags",\s+default: {}], output + end + + def test_supports_to_unsafe_h_values + assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new)) + end - def test_supports_to_unsafe_h_values - assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new)) + private + def assert_array_cycle(array) + # test creation + x = Hstore.create!(payload: array) + x.reload + assert_equal(array, x.payload) + + # test updating + x = Hstore.create!(payload: []) + x.payload = array + x.save! + x.reload + assert_equal(array, x.payload) end - private - def assert_array_cycle(array) - # test creation - x = Hstore.create!(payload: array) - x.reload - assert_equal(array, x.payload) - - # test updating - x = Hstore.create!(payload: []) - x.payload = array - x.save! - x.reload - assert_equal(array, x.payload) - end + def assert_cycle(hash) + # test creation + x = Hstore.create!(tags: hash) + x.reload + assert_equal(hash, x.tags) - def assert_cycle(hash) - # test creation - x = Hstore.create!(tags: hash) - x.reload - assert_equal(hash, x.tags) - - # test updating - x = Hstore.create!(tags: {}) - x.tags = hash - x.save! - x.reload - assert_equal(hash, x.tags) - end - end + # test updating + x = Hstore.create!(tags: {}) + x.tags = hash + x.save! + x.reload + assert_equal(hash, x.tags) + end end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 466d281e85..c24e0cb330 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -222,68 +222,66 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase connection.execute "DROP FUNCTION IF EXISTS my_uuid_generator();" end - if ActiveRecord::Base.connection.supports_extensions? - def test_id_is_uuid - assert_equal :uuid, UUID.columns_hash["id"].type - assert UUID.primary_key - end + def test_id_is_uuid + assert_equal :uuid, UUID.columns_hash["id"].type + assert UUID.primary_key + end - def test_id_has_a_default - u = UUID.create - assert_not_nil u.id - end + def test_id_has_a_default + u = UUID.create + assert_not_nil u.id + end - def test_auto_create_uuid - u = UUID.create - u.reload - assert_not_nil u.other_uuid - end + def test_auto_create_uuid + u = UUID.create + u.reload + assert_not_nil u.other_uuid + end - def test_pk_and_sequence_for_uuid_primary_key - pk, seq = connection.pk_and_sequence_for("pg_uuids") - assert_equal "id", pk - assert_nil seq - end + def test_pk_and_sequence_for_uuid_primary_key + pk, seq = connection.pk_and_sequence_for("pg_uuids") + assert_equal "id", pk + assert_nil seq + end - def test_schema_dumper_for_uuid_primary_key - schema = dump_table_schema "pg_uuids" - assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: -> { "uuid_generate_v1\(\)" }/, schema) - assert_match(/t\.uuid "other_uuid", default: -> { "uuid_generate_v4\(\)" }/, schema) - end + def test_schema_dumper_for_uuid_primary_key + schema = dump_table_schema "pg_uuids" + assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: -> { "uuid_generate_v1\(\)" }/, schema) + assert_match(/t\.uuid "other_uuid", default: -> { "uuid_generate_v4\(\)" }/, schema) + end + + def test_schema_dumper_for_uuid_primary_key_with_custom_default + schema = dump_table_schema "pg_uuids_2" + assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: -> { "my_uuid_generator\(\)" }/, schema) + assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema) + end - def test_schema_dumper_for_uuid_primary_key_with_custom_default - schema = dump_table_schema "pg_uuids_2" - assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: -> { "my_uuid_generator\(\)" }/, schema) - assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema) + def test_schema_dumper_for_uuid_primary_key_default + schema = dump_table_schema "pg_uuids_3" + if connection.supports_pgcrypto_uuid? + assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "gen_random_uuid\(\)" }/, schema) + else + assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) end + end + + def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration + @verbose_was = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false - def test_schema_dumper_for_uuid_primary_key_default - schema = dump_table_schema "pg_uuids_3" - if connection.supports_pgcrypto_uuid? - assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "gen_random_uuid\(\)" }/, schema) - else - assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) + migration = Class.new(ActiveRecord::Migration[5.0]) do + def version; 101 end + def migrate(x) + create_table("pg_uuids_4", id: :uuid) end - end + end.new + ActiveRecord::Migrator.new(:up, [migration]).migrate - def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration - @verbose_was = ActiveRecord::Migration.verbose - ActiveRecord::Migration.verbose = false - - migration = Class.new(ActiveRecord::Migration[5.0]) do - def version; 101 end - def migrate(x) - create_table("pg_uuids_4", id: :uuid) - end - end.new - ActiveRecord::Migrator.new(:up, [migration]).migrate - - schema = dump_table_schema "pg_uuids_4" - assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) - ensure - drop_table "pg_uuids_4" - ActiveRecord::Migration.verbose = @verbose_was - end + schema = dump_table_schema "pg_uuids_4" + assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) + ensure + drop_table "pg_uuids_4" + ActiveRecord::Migration.verbose = @verbose_was end end @@ -302,38 +300,36 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase drop_table "pg_uuids" end - if ActiveRecord::Base.connection.supports_extensions? - def test_id_allows_default_override_via_nil - col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default - FROM pg_attribute a - LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum - WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first - assert_nil col_desc["default"] - end + def test_id_allows_default_override_via_nil + col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default + FROM pg_attribute a + LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum + WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first + assert_nil col_desc["default"] + end - def test_schema_dumper_for_uuid_primary_key_with_default_override_via_nil - schema = dump_table_schema "pg_uuids" - assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: nil/, schema) - end + def test_schema_dumper_for_uuid_primary_key_with_default_override_via_nil + schema = dump_table_schema "pg_uuids" + assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: nil/, schema) + end - def test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration - @verbose_was = ActiveRecord::Migration.verbose - ActiveRecord::Migration.verbose = false - - migration = Class.new(ActiveRecord::Migration[5.0]) do - def version; 101 end - def migrate(x) - create_table("pg_uuids_4", id: :uuid, default: nil) - end - end.new - ActiveRecord::Migrator.new(:up, [migration]).migrate - - schema = dump_table_schema "pg_uuids_4" - assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema) - ensure - drop_table "pg_uuids_4" - ActiveRecord::Migration.verbose = @verbose_was - end + def test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration + @verbose_was = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false + + migration = Class.new(ActiveRecord::Migration[5.0]) do + def version; 101 end + def migrate(x) + create_table("pg_uuids_4", id: :uuid, default: nil) + end + end.new + ActiveRecord::Migrator.new(:up, [migration]).migrate + + schema = dump_table_schema "pg_uuids_4" + assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema) + ensure + drop_table "pg_uuids_4" + ActiveRecord::Migration.verbose = @verbose_was end end @@ -367,23 +363,21 @@ class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase drop_table "pg_uuid_posts" end - if ActiveRecord::Base.connection.supports_extensions? - def test_collection_association_with_uuid - post = UuidPost.create! - comment = post.uuid_comments.create! - assert post.uuid_comments.find(comment.id) - end + def test_collection_association_with_uuid + post = UuidPost.create! + comment = post.uuid_comments.create! + assert post.uuid_comments.find(comment.id) + end - def test_find_with_uuid - UuidPost.create! - assert_raise ActiveRecord::RecordNotFound do - UuidPost.find(123456) - end + def test_find_with_uuid + UuidPost.create! + assert_raise ActiveRecord::RecordNotFound do + UuidPost.find(123456) end + end - def test_find_by_with_uuid - UuidPost.create! - assert_nil UuidPost.find_by(id: 789) - end + def test_find_by_with_uuid + UuidPost.create! + assert_nil UuidPost.find_by(id: 789) end end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 799a65c61e..60e3bc17f2 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -294,34 +294,32 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{t\.oid\s+"obj_id"$}, output end - if ActiveRecord::Base.connection.supports_extensions? - def test_schema_dump_includes_extensions - connection = ActiveRecord::Base.connection - - connection.stubs(:extensions).returns(["hstore"]) - output = perform_schema_dump - assert_match "# These are extensions that must be enabled", output - assert_match %r{enable_extension "hstore"}, output - - connection.stubs(:extensions).returns([]) - output = perform_schema_dump - assert_no_match "# These are extensions that must be enabled", output - assert_no_match %r{enable_extension}, output - end + def test_schema_dump_includes_extensions + connection = ActiveRecord::Base.connection + + connection.stubs(:extensions).returns(["hstore"]) + output = perform_schema_dump + assert_match "# These are extensions that must be enabled", output + assert_match %r{enable_extension "hstore"}, output + + connection.stubs(:extensions).returns([]) + output = perform_schema_dump + assert_no_match "# These are extensions that must be enabled", output + assert_no_match %r{enable_extension}, output + end - def test_schema_dump_includes_extensions_in_alphabetic_order - connection = ActiveRecord::Base.connection + def test_schema_dump_includes_extensions_in_alphabetic_order + connection = ActiveRecord::Base.connection - connection.stubs(:extensions).returns(["hstore", "uuid-ossp", "xml2"]) - output = perform_schema_dump - enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten - assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions + connection.stubs(:extensions).returns(["hstore", "uuid-ossp", "xml2"]) + output = perform_schema_dump + enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten + assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions - connection.stubs(:extensions).returns(["uuid-ossp", "xml2", "hstore"]) - output = perform_schema_dump - enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten - assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions - end + connection.stubs(:extensions).returns(["uuid-ossp", "xml2", "hstore"]) + output = perform_schema_dump + enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten + assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions end end -- cgit v1.2.3 From 510147c1c63ded90d8668e85f2150dd637543fa9 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 25 Oct 2017 07:43:06 +0900 Subject: Change `VENDOR_PATH` to `APP_ROOT` in `bin/yarn` This variable was initially used to hold the vendor directory. https://github.com/rails/rails/commit/3dac36b Therefore, the variable name `VENDOR_PATH` was appropriate. However, `package.json` is now placed in the root of the project. https://github.com/rails/rails/commit/8e9e943 Therefore, like other bin scripts, I think the variable name `APP_ROOT` is appropriate. https://github.com/rails/rails/blob/2c845f6b03ddf2aa233b00385d24d769a4a34fa6/railties/lib/rails/generators/rails/app/templates/bin/setup.tt#L5 https://github.com/rails/rails/blob/2c845f6b03ddf2aa233b00385d24d769a4a34fa6/railties/lib/rails/generators/rails/app/templates/bin/update.tt#L5 --- railties/lib/rails/generators/rails/app/templates/bin/yarn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn b/railties/lib/rails/generators/rails/app/templates/bin/yarn index c2f9b6768a..b4e4d95286 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/yarn +++ b/railties/lib/rails/generators/rails/app/templates/bin/yarn @@ -1,5 +1,5 @@ -VENDOR_PATH = File.expand_path('..', __dir__) -Dir.chdir(VENDOR_PATH) do +APP_ROOT = File.expand_path('..', __dir__) +Dir.chdir(APP_ROOT) do begin exec "yarnpkg #{ARGV.join(' ')}" rescue Errno::ENOENT -- cgit v1.2.3 From 4db3449d34cfad338114d00e760bb29f263916db Mon Sep 17 00:00:00 2001 From: willnet Date: Wed, 25 Oct 2017 13:29:18 +0900 Subject: [ci skip]Add space before closing curly brace --- guides/source/action_controller_overview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index d53c4dedf9..28f7246197 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -654,8 +654,8 @@ class UsersController < ApplicationController @users = User.all respond_to do |format| format.html # index.html.erb - format.xml { render xml: @users} - format.json { render json: @users} + format.xml { render xml: @users } + format.json { render json: @users } end end end -- cgit v1.2.3 From ab8c7a518eb9583dbc574ff68fd56bcccf383452 Mon Sep 17 00:00:00 2001 From: Paul Kuruvilla Date: Wed, 25 Oct 2017 14:33:51 +0530 Subject: Avoid using index_xinfo, only available in sqlite >= 3.8.9 --- .../sqlite3/schema_statements.rb | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb index 3ab9dee370..58e5138e02 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb @@ -19,14 +19,17 @@ module ActiveRecord /\sWHERE\s+(?.+)$/i =~ index_sql - columns = [] - orders = {} - exec_query("PRAGMA index_xinfo(#{quote(row['name'])})", "SCHEMA").each do |col| - # xinfo also lists non-key columns, let's filter those out - next if col["key"] == 0 + columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col| + col["name"] + end - columns << col["name"] - orders[col["name"]] = :desc if col["desc"] == 1 + # Add info on sort order for columns (only desc order is explicitly specified, asc is + # the default) + orders = {} + if index_sql # index_sql can be null in case of primary key indexes + index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column| + orders[order_column] = :desc + } end IndexDefinition.new( @@ -34,8 +37,8 @@ module ActiveRecord row["name"], row["unique"] != 0, columns, - orders: orders, - where: where + where: where, + orders: orders ) end end -- cgit v1.2.3 From 7e6cdc1c2f3c06f2829b74afd956aca04632025b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 23 Oct 2017 15:58:01 -0400 Subject: Require capybara 2.15 because we depend on the new puma integration --- actionpack/lib/action_dispatch/system_test_case.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb index 58cea7b779..78efba9eee 100644 --- a/actionpack/lib/action_dispatch/system_test_case.rb +++ b/actionpack/lib/action_dispatch/system_test_case.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -gem "capybara", "~> 2.13" +gem "capybara", "~> 2.15" require "capybara/dsl" require "capybara/minitest" -- cgit v1.2.3 From cf4ba2fa46933f6b450a95c14aa175eae4a4e9ba Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 25 Oct 2017 01:21:09 +0000 Subject: Implement `PostgreSQL::SchemaDumper#extensions` and abstract `SchemaDumper#extensions` is now an empty method. Since #30337, every database adapter has its own `SchemaDumper`. `extensions` are only supported by PostgreSQL database and postgresql database adapter. --- .../connection_adapters/postgresql/schema_dumper.rb | 12 ++++++++++++ activerecord/lib/active_record/schema_dumper.rb | 10 +--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb index c0dbb166b7..84643d20da 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb @@ -5,6 +5,18 @@ module ActiveRecord module PostgreSQL class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc: private + + def extensions(stream) + extensions = @connection.extensions + if extensions.any? + stream.puts " # These are extensions that must be enabled in order to support this database" + extensions.sort.each do |extension| + stream.puts " enable_extension #{extension.inspect}" + end + stream.puts + end + end + def prepare_column_options(column) spec = super spec[:array] = "true" if column.array? diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 8d0311fabd..66f7d29886 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -82,16 +82,8 @@ HEADER stream.puts "end" end + # extensions are only supported by PostgreSQL def extensions(stream) - return unless @connection.supports_extensions? - extensions = @connection.extensions - if extensions.any? - stream.puts " # These are extensions that must be enabled in order to support this database" - extensions.sort.each do |extension| - stream.puts " enable_extension #{extension.inspect}" - end - stream.puts - end end def tables(stream) -- cgit v1.2.3 From 7b51b140d5da80402a114c06f30fbf66e12c676e Mon Sep 17 00:00:00 2001 From: Ryan Perez Date: Sun, 8 Mar 2015 01:36:29 +0000 Subject: Fixed functionality to include method in params_wrapper.rb to properly wrap all attributes, including those which are nested. --- actionpack/lib/action_controller/metal/params_wrapper.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index f4f2381286..9f79d796cc 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -113,6 +113,13 @@ module ActionController self.include = m.attribute_names end end + if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.any? + nested_attributes_names = self.nested_attributes_options.keys.map do |key| + key.to_s.concat('_attributes').to_sym + end + self.include += nested_attributes_names + end + end end end -- cgit v1.2.3 From dd423a743e9d47aaa3d15e4f7dc0fc64157cd55c Mon Sep 17 00:00:00 2001 From: Kelton Manzanares Date: Fri, 20 Oct 2017 19:07:14 -0600 Subject: checking for nested attributes when attribute names specified to wrap them as well --- actionpack/lib/action_controller/metal/params_wrapper.rb | 13 +++++++------ actionpack/test/controller/params_wrapper_test.rb | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 9f79d796cc..a678377d4f 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -112,14 +112,15 @@ module ActionController else self.include = m.attribute_names end - end - if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.any? - nested_attributes_names = self.nested_attributes_options.keys.map do |key| - key.to_s.concat('_attributes').to_sym + + if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any? + self.include += m.nested_attributes_options.keys.map do |key| + key.to_s.concat("_attributes") + end end - self.include += nested_attributes_names - end + self.include + end end end end diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index df68ef25a3..41b583d1a7 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -255,6 +255,20 @@ class ParamsWrapperTest < ActionController::TestCase assert_equal "", @response.body end end + + def test_derived_wrapped_keys_from_nested_attributes + def User.nested_attributes_options + { person: {} } + end + + assert_called(User, :attribute_names, times: 2, returns: ["username"]) do + with_default_wrapper_options do + @request.env["CONTENT_TYPE"] = "application/json" + post :parse, params: { "username" => "sikachu", "person_attributes" => { "title" => "Developer" } } + assert_parameters("username" => "sikachu", "person_attributes" => { "title" => "Developer" }, "user" => { "username" => "sikachu", "person_attributes" => { "title" => "Developer" } }) + end + end + end end class NamespacedParamsWrapperTest < ActionController::TestCase -- cgit v1.2.3 From 55fdb125db82da0ff5b17466f226d96661df0cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=AA=E0=A5=8D=E0=A4=B0=E0=A4=A5=E0=A4=AE=E0=A5=87?= =?UTF-8?q?=E0=A4=B6=20Sonpatki?= Date: Wed, 25 Oct 2017 22:56:38 +0530 Subject: Remove CHANGELOG entry that was backported to Rails 5.1.3. [ci skip] (#30986) - Backport commit: https://github.com/rails/rails/commit/7122a2cdc3634e170129f8b6cabd1e8fbed13c3d --- actioncable/CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md index 4355c97df0..5b9cc84c09 100644 --- a/actioncable/CHANGELOG.md +++ b/actioncable/CHANGELOG.md @@ -24,11 +24,4 @@ *Marc Rendl Ignacio* -* Action Cable socket errors are now logged to the console - - Previously any socket errors were ignored and this made it hard to diagnose socket issues (e.g. as discussed in #28362). - - *Edward Poot* - - Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/actioncable/CHANGELOG.md) for previous changes. -- cgit v1.2.3 From 07ee01c153865a0cdfad9068b096bf5c4443c396 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 26 Oct 2017 03:19:37 +0900 Subject: Third party adapters doesn't support index orders yet --- activerecord/test/cases/schema_dumper_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index a03fd39abc..ac5092f1c1 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -177,7 +177,7 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_schema_dumps_index_columns_in_right_order index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_index/).first.strip - if current_adapter?(:PostgreSQLAdapter) + if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition elsif current_adapter?(:Mysql2Adapter) if ActiveRecord::Base.connection.supports_index_sort_order? @@ -186,7 +186,7 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }', index_definition end else - assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition + assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition end end -- cgit v1.2.3 From e5ce7bcbae1a9eaa40d9e094ae43e3b9226324cf Mon Sep 17 00:00:00 2001 From: Joe Van Dyk Date: Wed, 25 Oct 2017 19:52:21 -0700 Subject: remove incorrect statement about serializable transactions using serializable isolation would prevent the duplicate insert as done in the example from happening --- activerecord/lib/active_record/validations/uniqueness.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index f4ad58c087..4c2c5dd852 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -205,9 +205,7 @@ module ActiveRecord # | # Boom! We now have a duplicate # | # title! # - # This could even happen if you use transactions with the 'serializable' - # isolation level. The best way to work around this problem is to add a unique - # index to the database table using + # The best way to work around this problem is to add a unique index to the database table using # {connection.add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index]. # In the rare case that a race condition occurs, the database will guarantee # the field's uniqueness. -- cgit v1.2.3 From 3b66804c81c0698e96ceebd7dd02fa0dc4c54e26 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 26 Oct 2017 14:32:23 +0900 Subject: Remove mention about Evented Redis [ci skip] Evented Redis adapter was removed in 48766e32d31651606b9f68a16015ad05c3b0de2c. --- guides/source/action_cable_overview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md index 6ac6792e26..57403a4bf9 100644 --- a/guides/source/action_cable_overview.md +++ b/guides/source/action_cable_overview.md @@ -668,8 +668,8 @@ authentication. You can see one way of doing that with Devise in this [article]( ## Dependencies Action Cable provides a subscription adapter interface to process its -pubsub internals. By default, asynchronous, inline, PostgreSQL, evented -Redis, and non-evented Redis adapters are included. The default adapter +pubsub internals. By default, asynchronous, inline, PostgreSQL, and Redis +adapters are included. The default adapter in new Rails applications is the asynchronous (`async`) adapter. The Ruby side of things is built on top of [websocket-driver](https://github.com/faye/websocket-driver-ruby), -- cgit v1.2.3 From b8b089ef11300fab4625011b942ee4a1fbbdbbbd Mon Sep 17 00:00:00 2001 From: Matt Rohrer Date: Thu, 21 Sep 2017 15:39:49 +0200 Subject: Allow passing a Proc or Symbol as an argument to length validator values This brings the Length validator in line with the Numericality validator, which currently supports Proc & Symbol arguments --- activemodel/CHANGELOG.md | 4 +++ activemodel/lib/active_model/validations/length.rb | 10 +++++-- .../cases/validations/length_validation_test.rb | 31 ++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 048c43f2c4..82e3a7f4dd 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,3 +1,7 @@ +* Allow passing a Proc or Symbol to length validator options. + + *Matt Rohrer* + * Add method `#merge!` for `ActiveModel::Errors`. *Jahfer Husain* diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index d1a4197286..d6c80b2c5d 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -31,8 +31,8 @@ module ActiveModel keys.each do |key| value = options[key] - unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY - raise ArgumentError, ":#{key} must be a nonnegative Integer or Infinity" + unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY || value.is_a?(Symbol) || value.is_a?(Proc) + raise ArgumentError, ":#{key} must be a nonnegative Integer, Infinity, Symbol, or Proc" end end end @@ -45,6 +45,12 @@ module ActiveModel next unless check_value = options[key] if !value.nil? || skip_nil_check?(key) + case check_value + when Proc + check_value = check_value.call(record) + when Symbol + check_value = record.send(check_value) + end next if value_length.send(validity_check, check_value) end diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb index a0d8e058f5..42f76f3e3c 100644 --- a/activemodel/test/cases/validations/length_validation_test.rb +++ b/activemodel/test/cases/validations/length_validation_test.rb @@ -410,4 +410,35 @@ class LengthValidationTest < ActiveModel::TestCase assert Topic.new("title" => "david").valid? assert Topic.new("title" => "david2").invalid? end + + def test_validates_length_of_using_proc_as_maximum + Topic.validates_length_of :title, maximum: ->(model) { 5 } + + t = Topic.new("title" => "valid", "content" => "whatever") + assert t.valid? + + t.title = "notvalid" + assert t.invalid? + assert t.errors[:title].any? + assert_equal ["is too long (maximum is 5 characters)"], t.errors[:title] + + t.title = "" + assert t.valid? + end + + def test_validates_length_of_using_proc_as_maximum_with_model_method + Topic.send(:define_method, :max_title_length, lambda { 5 }) + Topic.validates_length_of :title, maximum: Proc.new(&:max_title_length) + + t = Topic.new("title" => "valid", "content" => "whatever") + assert t.valid? + + t.title = "notvalid" + assert t.invalid? + assert t.errors[:title].any? + assert_equal ["is too long (maximum is 5 characters)"], t.errors[:title] + + t.title = "" + assert t.valid? + end end -- cgit v1.2.3 From d95fbd7dba54cfbd282f808102dfdffa3499d669 Mon Sep 17 00:00:00 2001 From: pavel Date: Thu, 26 Oct 2017 23:16:54 +0200 Subject: fix initial count --- activerecord/lib/active_record/associations/alias_tracker.rb | 2 +- .../test/cases/associations/has_many_through_associations_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index 07bd0a273b..14881cfe17 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -33,7 +33,7 @@ module ActiveRecord elsif join.respond_to? :left join.left.name == name ? 1 : 0 elsif join.is_a?(Hash) - join[name] + join.fetch(name, 0) else # this branch is reached by two tests: # 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 c6a4ac356f..1dbc407fd1 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1258,6 +1258,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments end + def test_has_many_trough_with_scope_should_accept_string_and_hash_join + assert_equal authors(:david), Author.joins({ comments_for_first_author: :post }, "inner join posts posts_alias on authors.id = posts_alias.author_id").eager_load(:categories).take + end + def test_has_many_through_with_scope_should_respect_table_alias family = Family.create! users = 3.times.map { User.create! } -- cgit v1.2.3 From a8966cc4701dcdd982e291d63b27be736c777fc8 Mon Sep 17 00:00:00 2001 From: pavel Date: Fri, 27 Oct 2017 01:22:16 +0200 Subject: delegate scope for --- activerecord/lib/active_record/reflection.rb | 2 +- .../test/cases/associations/nested_through_associations_test.rb | 5 +++++ activerecord/test/models/author.rb | 2 ++ activerecord/test/models/post.rb | 1 + activerecord/test/models/tag.rb | 1 + activerecord/test/models/tagging.rb | 1 + 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 8877f762b2..c18214a842 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1002,7 +1002,7 @@ module ActiveRecord end class PolymorphicReflection < AbstractReflection # :nodoc: - delegate :klass, :scope, :plural_name, :type, :get_join_keys, to: :@reflection + delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection def initialize(reflection, previous_reflection) @reflection = reflection diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 3e37e512ca..ab3bf5eb8d 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -425,6 +425,11 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase assert authors.empty? end + def test_nested_has_many_through_with_scope_on_polymorphic_reflection + authors = Author.joins(:ordered_posts).where("posts.id" => posts(:misc_by_bob).id) + assert_equal [authors(:mary), authors(:bob)], authors.distinct.sort_by(&:id) + end + def test_has_many_through_with_foreign_key_option_on_through_reflection assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order("posts.id") assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index e9eba9be2e..cb8686f315 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -101,10 +101,12 @@ class Author < ActiveRecord::Base has_many :taggings, through: :posts, source: :taggings has_many :taggings_2, through: :posts, source: :tagging has_many :tags, through: :posts + has_many :ordered_tags, through: :posts has_many :post_categories, through: :posts, source: :categories has_many :tagging_tags, through: :taggings, source: :tag has_many :similar_posts, -> { distinct }, through: :tags, source: :tagged_posts + has_many :ordered_posts, -> { distinct }, through: :ordered_tags, source: :tagged_posts has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, through: :posts, source: :tags has_many :tags_with_primary_key, through: :posts diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 935a11e811..7f064bf3dd 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -115,6 +115,7 @@ class Post < ActiveRecord::Base has_many :misc_tags, -> { where tags: { name: "Misc" } }, through: :taggings, source: :tag has_many :funky_tags, through: :taggings, source: :tag has_many :super_tags, through: :taggings + has_many :ordered_tags, through: :taggings has_many :tags_with_primary_key, through: :taggings, source: :tag_with_primary_key has_one :tagging, as: :taggable diff --git a/activerecord/test/models/tag.rb b/activerecord/test/models/tag.rb index 4495ac4a09..bc13c3a42d 100644 --- a/activerecord/test/models/tag.rb +++ b/activerecord/test/models/tag.rb @@ -12,4 +12,5 @@ class OrderedTag < Tag self.table_name = "tags" has_many :taggings, -> { order("taggings.id DESC") }, foreign_key: "tag_id" + has_many :tagged_posts, through: :taggings, source: "taggable", source_type: "Post" end diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb index fc0af026c5..861fde633f 100644 --- a/activerecord/test/models/tagging.rb +++ b/activerecord/test/models/tagging.rb @@ -8,6 +8,7 @@ class Tagging < ActiveRecord::Base belongs_to :tag, -> { includes(:tagging) } belongs_to :super_tag, class_name: "Tag", foreign_key: "super_tag_id" belongs_to :invalid_tag, class_name: "Tag", foreign_key: "tag_id" + belongs_to :ordered_tag, class_name: "OrderedTag", foreign_key: "tag_id" belongs_to :blue_tag, -> { where tags: { name: "Blue" } }, class_name: "Tag", foreign_key: :tag_id belongs_to :tag_with_primary_key, class_name: "Tag", foreign_key: :tag_id, primary_key: :custom_primary_key belongs_to :taggable, polymorphic: true, counter_cache: :tags_count -- cgit v1.2.3 From f5f0b49b9b233f85664d7a464c18afb2f3f10692 Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Thu, 26 Oct 2017 18:17:38 -0700 Subject: Prevent extra string allocations when no 'rel' arg passed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do a check if the 'rel' argument is passed in, and simply set it to 'nofollow' if 'rel' was not passed in. This prevents three string allocations for each call to `link_to` in that scenario. In the scenario where the 'rel' argument is passed in, performance is around the same as before as the `key?` check is very fast. ```ruby begin require "bundler/inline" rescue LoadError => e $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler" raise e end gemfile(true) do source "https://rubygems.org" gem "benchmark-ips" gem "rails" end def allocate_count GC.disable before = ObjectSpace.count_objects yield after = ObjectSpace.count_objects after.each { |k,v| after[k] = v - before[k] } after[:T_HASH] -= 1 # probe effect - we created the before hash. GC.enable result = after.reject { |k,v| v == 0 } GC.start result end @hash = {} def master_version "#{@hash['rel'.freeze]} nofollow".lstrip end def fast_version if @hash.key?('rel'.freeze) "#{@hash["rel"]} nofollow".lstrip else "nofollow".freeze end end puts 'no rel key' puts "master_version" puts allocate_count { 1000.times { master_version } } puts "fast_version" puts allocate_count { 1000.times { fast_version } } Benchmark.ips do |x| x.report("master_version") { master_version } x.report("fast_version") { fast_version } x.compare! end puts 'rel key' @hash['rel'] = 'hi'.freeze puts "master_version" puts allocate_count { 1000.times { master_version } } puts "fast_version" puts allocate_count { 1000.times { fast_version } } Benchmark.ips do |x| x.report("master_version") { master_version } x.report("fast_version") { fast_version } x.compare! end ``` ``` no rel key master_version {:FREE=>-2791, :T_STRING=>3052} fast_version {:FREE=>-1} Warming up -------------------------------------- master_version 80.324k i/100ms fast_version 200.262k i/100ms Calculating ------------------------------------- master_version 2.049M (±11.9%) i/s - 10.121M in 5.025613s fast_version 6.645M (±21.3%) i/s - 29.439M in 5.007488s Comparison: fast_version: 6644506.3 i/s master_version: 2048833.0 i/s - 3.24x slower rel key master_version {:FREE=>-2001, :T_STRING=>2000} fast_version {:FREE=>-2001, :T_STRING=>2000} Warming up -------------------------------------- master_version 155.673k i/100ms fast_version 106.515k i/100ms Calculating ------------------------------------- master_version 2.652M (±20.4%) i/s - 12.610M in 5.036494s fast_version 2.237M (±16.8%) i/s - 10.865M in 5.035366s Comparison: master_version: 2651702.2 i/s fast_version: 2237470.6 i/s - same-ish: difference falls within error ``` --- actionview/lib/action_view/helpers/url_helper.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index efda549c0d..4e436433c4 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -589,10 +589,14 @@ module ActionView end def add_method_to_attributes!(html_options, method) - if method && method.to_s.downcase != "get".freeze && html_options["rel".freeze] !~ /nofollow/ - html_options["rel".freeze] = "#{html_options["rel".freeze]} nofollow".lstrip + if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/ + if html_options.key?("rel") + html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip + else + html_options["rel"] = "nofollow" + end end - html_options["data-method".freeze] = method + html_options["data-method"] = method end def token_tag(token = nil, form_options: {}) -- cgit v1.2.3 From e0025fe34d7befaaafe44f0ba3a4c0d8f6c60cc4 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 27 Oct 2017 16:39:40 +0900 Subject: Fix typo `s/trough/through/` --- .../test/cases/associations/has_many_through_associations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1dbc407fd1..ecebf922c1 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1258,7 +1258,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments end - def test_has_many_trough_with_scope_should_accept_string_and_hash_join + def test_has_many_through_with_scope_should_accept_string_and_hash_join assert_equal authors(:david), Author.joins({ comments_for_first_author: :post }, "inner join posts posts_alias on authors.id = posts_alias.author_id").eager_load(:categories).take end -- cgit v1.2.3 From c6115fea425ca0b6ce85dcab15be33166ad8ae9f Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 27 Oct 2017 16:45:07 +0900 Subject: Fix all `s/trough/through/` ``` % git grep -n trough activerecord/test/cases/associations/has_many_through_associations_test.rb:1253: def test_has_many_trough_with_scope_that_has_joined_same_table_with_parent_relation ``` --- .../test/cases/associations/has_many_through_associations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ecebf922c1..046020e310 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1250,7 +1250,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase TenantMembership.current_member = nil end - def test_has_many_trough_with_scope_that_has_joined_same_table_with_parent_relation + def test_has_many_through_with_scope_that_has_joined_same_table_with_parent_relation assert_equal authors(:david), Author.joins(:comments_for_first_author).take end -- cgit v1.2.3 From df505fb8104588af13c993c1dcd881398889dd30 Mon Sep 17 00:00:00 2001 From: Prathamesh Sonpatki Date: Fri, 27 Oct 2017 16:23:56 +0530 Subject: Remove CHANGELOG entry for PR #28681 [ci skip] - It is present in Rails 5.1.3 already. - Backport commit https://github.com/rails/rails/commit/9efa0b70a638dcfdc05b30ab530f89f2847bb9c2 --- activerecord/CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 3e1b467633..4aa7ecfc7e 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -347,10 +347,6 @@ *Ryuta Kamizono* -* Quote database name in `db:create` grant statement (when database user does not have access to create the database). - - *Rune Philosof* - * Raise error `UnknownMigrationVersionError` on the movement of migrations when the current migration does not exist. -- cgit v1.2.3 From 67550f16129a293a4bff76f4311b6eb552361aa1 Mon Sep 17 00:00:00 2001 From: Tyler Scoville Date: Thu, 26 Oct 2017 15:14:20 -0700 Subject: [ci skip] Updated 5_1_release_notes.md to include support to :text and :nothing in render --- guides/source/5_1_release_notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/guides/source/5_1_release_notes.md b/guides/source/5_1_release_notes.md index 80c9da6446..6b9a950a42 100644 --- a/guides/source/5_1_release_notes.md +++ b/guides/source/5_1_release_notes.md @@ -350,6 +350,10 @@ Please refer to the [Changelog][action-pack] for detailed changes. * Removed deprecated methods related to controller filters. ([Commit](https://github.com/rails/rails/commit/d7be30e8babf5e37a891522869e7b0191b79b757)) + +* Removed deprecated support to `:text` and `:nothing` in `render`. + ([Commit](https://github.com/rails/rails/commit/79a5ea9eadb4d43b62afacedc0706cbe88c54496), + [Commit](https://github.com/rails/rails/commit/57e1c99a280bdc1b324936a690350320a1cd8111)) ### Deprecations -- cgit v1.2.3 From d898f9e55cc294d5f4338c443e76220349f9093b Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Sat, 28 Oct 2017 00:39:00 +0100 Subject: Remove mention of X-Post-Data-Format header [ci skip] Support for this header was removed when `actionpack-xml_parser` was extracted, and has since been dropped from the gem. --- actionpack/lib/action_dispatch/http/mime_negotiation.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 0ca18d98a1..d7435fa8df 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -12,9 +12,6 @@ module ActionDispatch end # The MIME type of the HTTP request, such as Mime[:xml]. - # - # For backward compatibility, the post \format is extracted from the - # X-Post-Data-Format HTTP header if present. def content_mime_type fetch_header("action_dispatch.request.content_type") do |k| v = if get_header("CONTENT_TYPE") =~ /^([^,\;]*)/ -- cgit v1.2.3 From 8b22725c782a65b2222f7a9ffed6d241c599f2e6 Mon Sep 17 00:00:00 2001 From: ta1kt0me Date: Sat, 28 Oct 2017 11:51:21 +0900 Subject: Enable to call Rails.ajax without beforeSend --- .../assets/javascripts/rails-ujs/utils/ajax.coffee | 2 +- actionview/test/ujs/public/test/call-ajax.js | 27 ++++++++++++++++++++++ actionview/test/ujs/views/tests/index.html.erb | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 actionview/test/ujs/public/test/call-ajax.js diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee index 4d7848e162..cc0e037428 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee @@ -21,7 +21,7 @@ Rails.ajax = (options) -> options.error?(response, xhr.statusText, xhr) options.complete?(xhr, xhr.statusText) - unless options.beforeSend?(xhr, options) + if options.beforeSend? && !options.beforeSend(xhr, options) return false if xhr.readyState is XMLHttpRequest.OPENED diff --git a/actionview/test/ujs/public/test/call-ajax.js b/actionview/test/ujs/public/test/call-ajax.js new file mode 100644 index 0000000000..49e64cad5c --- /dev/null +++ b/actionview/test/ujs/public/test/call-ajax.js @@ -0,0 +1,27 @@ +(function() { + +module('call-ajax', { + setup: function() { + $('#qunit-fixture') + .append($('', { href: '#' })) + } +}) + +asyncTest('call ajax without "ajax:beforeSend"', 1, function() { + + var link = $('#qunit-fixture a') + link.bindNative('click', function() { + Rails.ajax({ + type: 'get', + url: '/', + success: function() { + ok(true, 'calling request in ajax:success') + } + }) + }) + + link.triggerNative('click') + setTimeout(function() { start() }, 13) +}) + +})() diff --git a/actionview/test/ujs/views/tests/index.html.erb b/actionview/test/ujs/views/tests/index.html.erb index 8de6cd0695..6b16535216 100644 --- a/actionview/test/ujs/views/tests/index.html.erb +++ b/actionview/test/ujs/views/tests/index.html.erb @@ -1,6 +1,6 @@ <% @title = "rails-ujs test" %> -<%= test_to 'data-confirm', 'data-remote', 'data-disable', 'data-disable-with', 'call-remote', 'call-remote-callbacks', 'data-method', 'override', 'csrf-refresh', 'csrf-token' %> +<%= test_to 'data-confirm', 'data-remote', 'data-disable', 'data-disable-with', 'call-remote', 'call-remote-callbacks', 'data-method', 'override', 'csrf-refresh', 'csrf-token', 'call-ajax' %>

<%= @title %>

-- cgit v1.2.3 From 65e08da68f6b344243f3c7bba0aee68342ee2228 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 28 Oct 2017 12:42:21 +0900 Subject: Add load hook for `ActionDispatch::SystemTestCase` This is useful to extend `SystemTestCase`. Also, since other test classes already have load hooks, should also be in `SystemTestCase`. Ref: 0510208dd1ff23baa619884c0abcae4d141fae53 --- actionpack/lib/action_dispatch/system_test_case.rb | 2 ++ guides/source/engines.md | 1 + 2 files changed, 3 insertions(+) diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb index 78efba9eee..7246e01cff 100644 --- a/actionpack/lib/action_dispatch/system_test_case.rb +++ b/actionpack/lib/action_dispatch/system_test_case.rb @@ -131,6 +131,8 @@ module ActionDispatch end driven_by :selenium + + ActiveSupport.run_load_hooks(:action_dispatch_system_test_case, self) end SystemTestCase.start_application diff --git a/guides/source/engines.md b/guides/source/engines.md index a9b841e3bf..b226eac347 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -1501,6 +1501,7 @@ To hook into the initialization process of one of the following classes use the | `ActionController::Base` | `action_controller` | | `ActionController::TestCase` | `action_controller_test_case` | | `ActionDispatch::IntegrationTest` | `action_dispatch_integration_test` | +| `ActionDispatch::SystemTestCase` | `action_dispatch_system_test_case` | | `ActionMailer::Base` | `action_mailer` | | `ActionMailer::TestCase` | `action_mailer_test_case` | | `ActionView::Base` | `action_view` | -- cgit v1.2.3 From 50f697664edf0d2deff22f3f1a1c8e01d54a74ca Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Sat, 28 Oct 2017 00:41:14 -0500 Subject: Puma Rack handler is required by Capybara See: https://github.com/teamcapybara/capybara/blob/7d693f068c44f6a460336da70fb6e9e5f94f3db9/lib/capybara.rb#L450 --- actionpack/lib/action_dispatch/system_testing/server.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/system_testing/server.rb b/actionpack/lib/action_dispatch/system_testing/server.rb index 32aa6a4dc4..8f1b6725b1 100644 --- a/actionpack/lib/action_dispatch/system_testing/server.rb +++ b/actionpack/lib/action_dispatch/system_testing/server.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "rack/handler/puma" - module ActionDispatch module SystemTesting class Server # :nodoc: -- cgit v1.2.3 From c40b4428e6d3885a8adc0ceba3aeac7599c14879 Mon Sep 17 00:00:00 2001 From: Shuhei Kitagawa Date: Sat, 28 Oct 2017 17:20:38 +0900 Subject: removed unnecessary returns --- actionmailer/test/caching_test.rb | 2 +- actionpack/lib/action_dispatch/journey/router.rb | 2 +- actionpack/test/controller/caching_test.rb | 2 +- actionview/lib/action_view/helpers/text_helper.rb | 4 ++-- activejob/test/support/delayed_job/delayed/backend/test.rb | 2 +- activerecord/lib/active_record/attribute_methods.rb | 2 +- activerecord/lib/active_record/counter_cache.rb | 2 +- activesupport/lib/active_support/concern.rb | 2 +- activesupport/lib/active_support/dependencies.rb | 2 +- activesupport/lib/active_support/duration/iso8601_parser.rb | 2 +- activesupport/lib/active_support/testing/isolation.rb | 2 +- activesupport/test/core_ext/module/remove_method_test.rb | 8 ++++---- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb index e11e8d4676..1ddd1487a7 100644 --- a/actionmailer/test/caching_test.rb +++ b/actionmailer/test/caching_test.rb @@ -198,7 +198,7 @@ end class CacheHelperOutputBufferTest < BaseCachingTest class MockController def read_fragment(name, options) - return false + false end def write_fragment(name, fragment, options) diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb index b8fdde5475..30af3ff930 100644 --- a/actionpack/lib/action_dispatch/journey/router.rb +++ b/actionpack/lib/action_dispatch/journey/router.rb @@ -61,7 +61,7 @@ module ActionDispatch return [status, headers, body] end - return [404, { "X-Cascade" => "pass" }, ["Not Found"]] + [404, { "X-Cascade" => "pass" }, ["Not Found"]] end def recognize(rails_req) diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index e0300539c9..969e0edfc2 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -317,7 +317,7 @@ end class CacheHelperOutputBufferTest < ActionController::TestCase class MockController def read_fragment(name, options) - return false + false end def write_fragment(name, fragment, options) diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb index 3044a2c0ef..84d38aa416 100644 --- a/actionview/lib/action_view/helpers/text_helper.rb +++ b/actionview/lib/action_view/helpers/text_helper.rb @@ -422,7 +422,7 @@ module ActionView def to_s value = @values[@index].to_s @index = next_index - return value + value end private @@ -446,7 +446,7 @@ module ActionView # uses an instance variable of ActionView::Base. def get_cycle(name) @_cycles = Hash.new unless defined?(@_cycles) - return @_cycles[name] + @_cycles[name] end def set_cycle(name, cycle_object) diff --git a/activejob/test/support/delayed_job/delayed/backend/test.rb b/activejob/test/support/delayed_job/delayed/backend/test.rb index 4721c1cc17..9280a37a5c 100644 --- a/activejob/test/support/delayed_job/delayed/backend/test.rb +++ b/activejob/test/support/delayed_job/delayed/backend/test.rb @@ -77,7 +77,7 @@ module Delayed self.locked_by = worker end - return true + true end def self.db_time_now diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 891e556dc4..23d2aef214 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -236,7 +236,7 @@ module ActiveRecord return has_attribute?(name) end - return true + true end # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+. diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 5005d58f1c..ee4f818cbf 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -53,7 +53,7 @@ module ActiveRecord unscoped.where(primary_key => object.id).update_all(updates) end - return true + true end # A generic "counter updater" implementation, intended primarily to be diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index 32b5ca986b..b0a0d845e5 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -113,7 +113,7 @@ module ActiveSupport def append_features(base) if base.instance_variable_defined?(:@_dependencies) base.instance_variable_get(:@_dependencies) << self - return false + false else return false if base < self @_dependencies.each { |dep| base.include(dep) } diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 649b900187..82c10b3079 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -615,7 +615,7 @@ module ActiveSupport #:nodoc: return false if desc.is_a?(Module) && desc.anonymous? name = to_constant_name desc return false unless qualified_const_defined?(name) - return autoloaded_constants.include?(name) + autoloaded_constants.include?(name) end # Will the provided constant descriptor be unloaded? diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb index 9379ec7da6..1847eeaa86 100644 --- a/activesupport/lib/active_support/duration/iso8601_parser.rb +++ b/activesupport/lib/active_support/duration/iso8601_parser.rb @@ -118,7 +118,7 @@ module ActiveSupport raise_parsing_error "(only last part can be fractional)" end - return true + true end end end diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index 954197a3cc..fa9bebb181 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -55,7 +55,7 @@ module ActiveSupport write.close result = read.read Process.wait2(pid) - return result.unpack("m")[0] + result.unpack("m")[0] end end diff --git a/activesupport/test/core_ext/module/remove_method_test.rb b/activesupport/test/core_ext/module/remove_method_test.rb index dbf71b477d..8493be8d08 100644 --- a/activesupport/test/core_ext/module/remove_method_test.rb +++ b/activesupport/test/core_ext/module/remove_method_test.rb @@ -6,22 +6,22 @@ require "active_support/core_ext/module/remove_method" module RemoveMethodTests class A def do_something - return 1 + 1 end def do_something_protected - return 1 + 1 end protected :do_something_protected def do_something_private - return 1 + 1 end private :do_something_private class << self def do_something_else - return 2 + 2 end end end -- cgit v1.2.3 From 03dd47ff219098305a588e16d717813eebbfa7a4 Mon Sep 17 00:00:00 2001 From: Shuhei Kitagawa Date: Sat, 28 Oct 2017 17:39:58 +0900 Subject: removed unnecessary semicolons --- actionmailer/test/caching_test.rb | 12 ++++++------ actionpack/test/abstract_unit.rb | 2 +- actionpack/test/controller/caching_test.rb | 12 ++++++------ actionpack/test/controller/test_case_test.rb | 2 +- .../test/cases/adapters/postgresql/statement_pool_test.rb | 2 +- .../test/cases/adapters/sqlite3/statement_pool_test.rb | 2 +- activesupport/test/test_case_test.rb | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb index e11e8d4676..94a0c1fc7c 100644 --- a/actionmailer/test/caching_test.rb +++ b/actionmailer/test/caching_test.rb @@ -214,9 +214,9 @@ class CacheHelperOutputBufferTest < BaseCachingTest output_buffer = ActionView::OutputBuffer.new controller = MockController.new cache_helper = Class.new do - def self.controller; end; - def self.output_buffer; end; - def self.output_buffer=; end; + def self.controller; end + def self.output_buffer; end + def self.output_buffer=; end end cache_helper.extend(ActionView::Helpers::CacheHelper) @@ -235,9 +235,9 @@ class CacheHelperOutputBufferTest < BaseCachingTest output_buffer = ActiveSupport::SafeBuffer.new controller = MockController.new cache_helper = Class.new do - def self.controller; end; - def self.output_buffer; end; - def self.output_buffer=; end; + def self.controller; end + def self.output_buffer; end + def self.output_buffer=; end end cache_helper.extend(ActionView::Helpers::CacheHelper) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 34dc02bebf..5262e85a28 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -44,7 +44,7 @@ module Rails @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test") end - def root; end; + def root; end end end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index e0300539c9..d2bc0b012e 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -333,9 +333,9 @@ class CacheHelperOutputBufferTest < ActionController::TestCase output_buffer = ActionView::OutputBuffer.new controller = MockController.new cache_helper = Class.new do - def self.controller; end; - def self.output_buffer; end; - def self.output_buffer=; end; + def self.controller; end + def self.output_buffer; end + def self.output_buffer=; end end cache_helper.extend(ActionView::Helpers::CacheHelper) @@ -354,9 +354,9 @@ class CacheHelperOutputBufferTest < ActionController::TestCase output_buffer = ActiveSupport::SafeBuffer.new controller = MockController.new cache_helper = Class.new do - def self.controller; end; - def self.output_buffer; end; - def self.output_buffer=; end; + def self.controller; end + def self.output_buffer; end + def self.output_buffer=; end end cache_helper.extend(ActionView::Helpers::CacheHelper) diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 46369ebbb0..536c5ed97a 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -6,7 +6,7 @@ require "active_support/json/decoding" require "rails/engine" class TestCaseTest < ActionController::TestCase - def self.fixture_path; end; + def self.fixture_path; end class TestController < ActionController::Base def no_op diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb index a3eb4f9e67..fef4b02b04 100644 --- a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb @@ -23,7 +23,7 @@ module ActiveRecord assert_equal "bar", cache["foo"] pid = fork { - lookup = cache["foo"]; + lookup = cache["foo"] exit!(!lookup) } diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb index 42b3841d41..61002435a4 100644 --- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb @@ -10,7 +10,7 @@ class SQLite3StatementPoolTest < ActiveRecord::SQLite3TestCase assert_equal "bar", cache["foo"] pid = fork { - lookup = cache["foo"]; + lookup = cache["foo"] exit!(!lookup) } diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index 65b1cb4a14..9bc9183668 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -87,7 +87,7 @@ class AssertDifferenceTest < ActiveSupport::TestCase def test_expression_is_evaluated_in_the_appropriate_scope silence_warnings do - local_scope = "foo"; + local_scope = "foo" local_scope = local_scope # to suppress unused variable warning assert_difference("local_scope; @object.num") { @object.increment } end -- cgit v1.2.3 From f50aeba01ee14f8fcd9fd98ec75b26cd5ef7aea8 Mon Sep 17 00:00:00 2001 From: Matthew Draper Date: Sat, 28 Oct 2017 22:44:06 +1030 Subject: Keep rubocop happy with the new Selenium runner --- ci/qunit-selenium-runner.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ci/qunit-selenium-runner.rb b/ci/qunit-selenium-runner.rb index 9b856e2a41..3a58377d77 100644 --- a/ci/qunit-selenium-runner.rb +++ b/ci/qunit-selenium-runner.rb @@ -1,9 +1,11 @@ -require 'qunit/selenium/test_runner' -require 'chromedriver/helper' +# frozen_string_literal: true + +require "qunit/selenium/test_runner" +require "chromedriver/helper" driver_options = Selenium::WebDriver::Chrome::Options.new -driver_options.add_argument('--headless') -driver_options.add_argument('--disable-gpu') +driver_options.add_argument("--headless") +driver_options.add_argument("--disable-gpu") driver = ::Selenium::WebDriver.for(:chrome, options: driver_options) result = QUnit::Selenium::TestRunner.new(driver).open(ARGV[0], timeout: 60) -- cgit v1.2.3 From cd4cbfccea281b670e527401b0e8572746e39a5d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 27 Oct 2017 23:36:09 +0900 Subject: Ordinal methods should respect loaded records We should reset partially loaded `@offsets` cache when latest records has loaded because the cache has been staled and it may not be consistent with latest records. --- activerecord/lib/active_record/relation.rb | 1 + activerecord/test/cases/finder_test.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 997cfe4b5e..c16d89aa00 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -594,6 +594,7 @@ module ActiveRecord end @records.each(&:readonly!) if readonly_value + @offsets = {} unless @offsets.empty? @loaded = true @records diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index d8bc917e7f..d03af7f111 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -676,6 +676,22 @@ class FinderTest < ActiveRecord::TestCase assert_kind_of Array, Topic.last(5) end + def test_first_should_respect_loaded_records + authors = Author.order(:name) + + assert_equal authors(:bob), authors.first + + aaron = authors.create!(name: "Aaron") + + authors.load + + assert_no_queries do + assert_equal aaron, authors.first + assert_equal authors(:bob), authors.second + assert_not_equal authors.first, authors.second + end + end + def test_unexisting_record_exception_handling assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1).parent -- cgit v1.2.3 From aa0865a8f07f889006a8b2ab1849aad6ab5f8fea Mon Sep 17 00:00:00 2001 From: suginoy Date: Sat, 28 Oct 2017 23:38:15 +0900 Subject: [ci skip]Update the documentation about the primary key type Replace the primary key type `integer` in docs with `bigint`. ref #26266 --- activerecord/README.rdoc | 2 +- activerecord/lib/active_record/associations.rb | 6 ++--- .../abstract/schema_statements.rb | 30 +++++++++++----------- .../active_record/connection_adapters/column.rb | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index ae53ecd177..ba83a9adb2 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -26,7 +26,7 @@ The Product class is automatically mapped to the table named "products", which might look like this: CREATE TABLE products ( - id int NOT NULL auto_increment, + id bigint NOT NULL auto_increment, name varchar(255), PRIMARY KEY (id) ); diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 573f41d7ad..bc7e288500 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -483,14 +483,14 @@ module ActiveRecord # The tables for these classes could look something like: # # CREATE TABLE users ( - # id int NOT NULL auto_increment, - # account_id int default NULL, + # id bigint NOT NULL auto_increment, + # account_id bigint default NULL, # name varchar default NULL, # PRIMARY KEY (id) # ) # # CREATE TABLE accounts ( - # id int NOT NULL auto_increment, + # id bigint NOT NULL auto_increment, # name varchar default NULL, # PRIMARY KEY (id) # ) 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 6698047ad6..9b7345f7c3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -216,7 +216,7 @@ module ActiveRecord # generates: # # CREATE TABLE suppliers ( - # id int auto_increment PRIMARY KEY + # id bigint auto_increment PRIMARY KEY # ) ENGINE=InnoDB DEFAULT CHARSET=utf8 # # ====== Rename the primary key column @@ -228,7 +228,7 @@ module ActiveRecord # generates: # # CREATE TABLE objects ( - # guid int auto_increment PRIMARY KEY, + # guid bigint auto_increment PRIMARY KEY, # name varchar(80) # ) # @@ -255,8 +255,8 @@ module ActiveRecord # generates: # # CREATE TABLE order ( - # product_id integer NOT NULL, - # client_id integer NOT NULL + # product_id bigint NOT NULL, + # client_id bigint NOT NULL # ); # # ALTER TABLE ONLY "orders" @@ -265,15 +265,15 @@ module ActiveRecord # ====== Do not add a primary key column # # create_table(:categories_suppliers, id: false) do |t| - # t.column :category_id, :integer - # t.column :supplier_id, :integer + # t.column :category_id, :bigint + # t.column :supplier_id, :bigint # end # # generates: # # CREATE TABLE categories_suppliers ( - # category_id int, - # supplier_id int + # category_id bigint, + # supplier_id bigint # ) # # ====== Create a temporary table based on a query @@ -361,8 +361,8 @@ module ActiveRecord # generates: # # CREATE TABLE assemblies_parts ( - # assembly_id int NOT NULL, - # part_id int NOT NULL, + # assembly_id bigint NOT NULL, + # part_id bigint NOT NULL, # ) ENGINE=InnoDB DEFAULT CHARSET=utf8 # def create_join_table(table_1, table_2, column_options: {}, **options) @@ -432,7 +432,7 @@ module ActiveRecord # t.references :company # end # - # Creates a company_id(integer) column. + # Creates a company_id(bigint) column. # # ====== Add a polymorphic foreign key column # @@ -440,7 +440,7 @@ module ActiveRecord # t.belongs_to :company, polymorphic: true # end # - # Creates company_type(varchar) and company_id(integer) columns. + # Creates company_type(varchar) and company_id(bigint) columns. # # ====== Remove a column # @@ -811,14 +811,14 @@ module ActiveRecord indexes(table_name).detect { |i| i.name == index_name } end - # Adds a reference. The reference column is an integer by default, + # Adds a reference. The reference column is a bigint by default, # the :type option can be used to specify a different type. # Optionally adds a +_type+ column, if :polymorphic option is provided. # #add_reference and #add_belongs_to are acceptable. # # The +options+ hash can include the following keys: # [:type] - # The reference column type. Defaults to +:integer+. + # The reference column type. Defaults to +:bigint+. # [:index] # Add an appropriate index. Defaults to true. # See #add_index for usage of this option. @@ -829,7 +829,7 @@ module ActiveRecord # [:null] # Whether the column allows nulls. Defaults to true. # - # ====== Create a user_id integer column + # ====== Create a user_id bigint column # # add_reference(:products, :user) # diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 28d949b503..5d81de9fe1 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -11,7 +11,7 @@ module ActiveRecord # Instantiates a new column in the table. # - # +name+ is the column's name, such as supplier_id in supplier_id int. + # +name+ is the column's name, such as supplier_id in supplier_id bigint. # +default+ is the type-casted default value, such as +new+ in sales_stage varchar(20) default 'new'. # +sql_type_metadata+ is various information about the type of the column # +null+ determines if this column allows +NULL+ values. -- cgit v1.2.3 From b2545e4106c8388cb2a4d9e06c31954e5ee2c948 Mon Sep 17 00:00:00 2001 From: Nick LaMuro Date: Mon, 23 Oct 2017 11:11:59 -0500 Subject: Deprecate ActiveSupport::Inflector#acronym_regex To be removed in Rails 6.0 (default for the deprecate helper). Code moved around as well for the ActiveSupport::Deprecation modules, since it was dependent on ActiveSupport::Inflector being loaded for it to work. By "lazy loading" the Inflector code from within the Deprecation code, we can require ActiveSupport::Deprecation from ActiveSupport::Inflector and not get a circular dependency issue. --- actionpack/test/dispatch/request_test.rb | 2 +- activesupport/lib/active_support/deprecation/constant_accessor.rb | 4 ++-- activesupport/lib/active_support/deprecation/proxy_wrappers.rb | 3 ++- activesupport/lib/active_support/inflector/inflections.rb | 6 ++++-- activesupport/test/inflector_test.rb | 6 ++++++ 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 2057aecbf9..8661dc56d6 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -763,7 +763,7 @@ class RequestMethod < BaseRequestTest test "post uneffected by local inflections" do existing_acronyms = ActiveSupport::Inflector.inflections.acronyms.dup - existing_acronym_regex = ActiveSupport::Inflector.inflections.acronym_regex.dup + assert_deprecated { ActiveSupport::Inflector.inflections.acronym_regex.dup } begin ActiveSupport::Inflector.inflections do |inflect| inflect.acronym "POS" diff --git a/activesupport/lib/active_support/deprecation/constant_accessor.rb b/activesupport/lib/active_support/deprecation/constant_accessor.rb index 3d7eedf637..dd515cd6f4 100644 --- a/activesupport/lib/active_support/deprecation/constant_accessor.rb +++ b/activesupport/lib/active_support/deprecation/constant_accessor.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "active_support/inflector/methods" - module ActiveSupport class Deprecation # DeprecatedConstantAccessor transforms a constant into a deprecated one by @@ -29,6 +27,8 @@ module ActiveSupport # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"] module DeprecatedConstantAccessor def self.included(base) + require "active_support/inflector/methods" + extension = Module.new do def const_missing(missing_const_name) if class_variable_defined?(:@@_deprecated_constants) diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index f6c6648917..782ad2519c 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "active_support/inflector/methods" require "active_support/core_ext/regexp" module ActiveSupport @@ -125,6 +124,8 @@ module ActiveSupport # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"] class DeprecatedConstantProxy < DeprecationProxy def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.") + require "active_support/inflector/methods" + @old_const = old_const @new_const = new_const @deprecator = deprecator diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 6cfa8bb5aa..0450a4be4c 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -4,6 +4,7 @@ require "concurrent/map" require "active_support/core_ext/array/prepend_and_append" require "active_support/core_ext/regexp" require "active_support/i18n" +require "active_support/deprecation" module ActiveSupport module Inflector @@ -66,8 +67,9 @@ module ActiveSupport @__instance__[locale] ||= new end - attr_reader :plurals, :singulars, :uncountables, :humans, - :acronyms, :acronym_regex + attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex + deprecate :acronym_regex + attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc: def initialize diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index eeec0ab1a5..ad2ec1d67d 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -224,6 +224,12 @@ class InflectorTest < ActiveSupport::TestCase assert_equal("json_html_api", ActiveSupport::Inflector.underscore("JSONHTMLAPI")) end + def test_acronym_regexp_is_deprecated + assert_deprecated do + ActiveSupport::Inflector.inflections.acronym_regex + end + end + def test_underscore CamelToUnderscore.each do |camel, underscore| assert_equal(underscore, ActiveSupport::Inflector.underscore(camel)) -- cgit v1.2.3 From 46a7b2e20c24f90591495f28786774132f324dbc Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 30 Oct 2017 21:29:26 +0900 Subject: `PolymorphicReflection#scopes` is no longer used since a5651eb5 --- activerecord/lib/active_record/reflection.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index c18214a842..8633809757 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1009,11 +1009,6 @@ module ActiveRecord @previous_reflection = previous_reflection end - def scopes - scopes = @previous_reflection.scopes - scopes << @previous_reflection.source_type_scope - end - def join_scopes(table, predicate_builder) # :nodoc: scopes = @previous_reflection.join_scopes(table, predicate_builder) + super scopes << @previous_reflection.source_type_scope -- cgit v1.2.3 From 13c5aa818e9284fe30f83469b340e579195bda3f Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 30 Oct 2017 22:06:12 +0900 Subject: `source_type_scope` should respect correct table alias `join_scopes` in `PolymorphicReflection` is passed aliased `table`, so it should be respected for `source_type_scope`. Closes #13969. Fixes #13920. Fixes #15190. --- activerecord/lib/active_record/reflection.rb | 10 +++------- .../cases/associations/nested_through_associations_test.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 8633809757..87bfd75bca 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -839,10 +839,6 @@ module ActiveRecord source_reflection.join_scopes(table, predicate_builder) + super end - def source_type_scope - through_reflection.klass.where(foreign_type => options[:source_type]) - end - def has_scope? scope || options[:source_type] || source_reflection.has_scope? || @@ -1011,15 +1007,15 @@ module ActiveRecord def join_scopes(table, predicate_builder) # :nodoc: scopes = @previous_reflection.join_scopes(table, predicate_builder) + super - scopes << @previous_reflection.source_type_scope + scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope) end def constraints - @reflection.constraints + [source_type_info] + @reflection.constraints + [source_type_scope] end private - def source_type_info + def source_type_scope type = @previous_reflection.foreign_type source_type = @previous_reflection.options[:source_type] lambda { |object| where(type => source_type) } diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index ab3bf5eb8d..4cf5a9ffc4 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -24,6 +24,11 @@ require "models/category" require "models/categorization" require "models/membership" require "models/essay" +require "models/hotel" +require "models/department" +require "models/chef" +require "models/cake_designer" +require "models/drink_designer" class NestedThroughAssociationsTest < ActiveRecord::TestCase fixtures :authors, :author_addresses, :books, :posts, :subscriptions, :subscribers, :tags, :taggings, @@ -574,6 +579,15 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase assert !c.post_taggings.empty? end + def test_polymorphic_has_many_through_joined_different_table_twice + cake_designer = CakeDesigner.create!(chef: Chef.new) + drink_designer = DrinkDesigner.create!(chef: Chef.new) + department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef]) + hotel = Hotel.create!(departments: [department]) + + assert_equal hotel, Hotel.joins(:cake_designers, :drink_designers).take + end + private def assert_includes_and_joins_equal(query, expected, association) -- cgit v1.2.3 From c4867b88f6eed16230a90077b82ad02293ea45b3 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Fri, 25 Aug 2017 23:05:21 +0300 Subject: Raise error if unsupported charset for mysql `blog$ bin/rails db:create` Before: ``` Couldn't create database for {"adapter"=>"mysql2", "encoding"=>"utf42", "pool"=>5, "username"=>"root", "password"=>nil, "socket"=>"/var/run/mysqld/mysqld.sock", "database"=>"blog_development"}, {:charset=>"utf42"} (If you set the charset manually, make sure you have a matching collation) Created database 'blog_development' Couldn't create database for {"adapter"=>"mysql2", "encoding"=>"utf42", "pool"=>5, "username"=>"root", "password"=>nil, "socket"=>"/var/run/mysqld/mysqld.sock", "database"=>"blog_test"}, {:charset=>"utf42"} (If you set the charset manually, make sure you have a matching collation) Created database 'blog_test' ``` After: ``` Unsupported charset: '"utf42"' Couldn't create database for {"adapter"=>"mysql2", "encoding"=>"utf42", "pool"=>5, "username"=>"root", "password"=>nil, "socket"=>"/var/run/mysqld/mysqld.sock", "database"=>"blog_development"} rails aborted! Mysql2::Error: Unsupported charset: '"utf42"' ... (stack trace) ... bin/rails:4:in `
' Tasks: TOP => db:create (See full trace by running task with --trace) ``` Closes #29683 Related to #27398 --- activerecord/lib/active_record/tasks/mysql_database_tasks.rb | 4 +--- activerecord/test/cases/tasks/mysql_rake_test.rb | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index 2f91884f59..d6a46f7743 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -31,9 +31,7 @@ module ActiveRecord end establish_connection configuration else - $stderr.puts error.inspect - $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}" - $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration["encoding"] + raise end end diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index 98fe24baa0..6c9b0040ad 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -156,12 +156,12 @@ if current_adapter?(:Mysql2Adapter) ActiveRecord::Tasks::DatabaseTasks.create @configuration end - def test_sends_output_to_stderr_when_other_errors + def test_raises_error_when_other_errors @error.stubs(:errno).returns(42) - $stderr.expects(:puts).at_least_once.returns(nil) - - ActiveRecord::Tasks::DatabaseTasks.create @configuration + assert_raises(Mysql2::Error) do + ActiveRecord::Tasks::DatabaseTasks.create @configuration + end end private -- cgit v1.2.3 From a37d03c4b08451765acc721b51a4b5b7eab7e8cc Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sat, 26 Aug 2017 00:06:10 +0300 Subject: Simplify implementation of `MySQLDatabaseTasks` Don't process MySQL ERROR 1045, raise error instead Make behavior of `MySQLDatabaseTasks` more consistent with behavior of `PostgreSQLDatabaseTasks` --- .../lib/active_record/railties/jdbcmysql_error.rb | 18 ------ .../active_record/tasks/mysql_database_tasks.rb | 45 ------------- activerecord/test/cases/tasks/mysql_rake_test.rb | 75 +--------------------- 3 files changed, 3 insertions(+), 135 deletions(-) delete mode 100644 activerecord/lib/active_record/railties/jdbcmysql_error.rb diff --git a/activerecord/lib/active_record/railties/jdbcmysql_error.rb b/activerecord/lib/active_record/railties/jdbcmysql_error.rb deleted file mode 100644 index 72c75ddd52..0000000000 --- a/activerecord/lib/active_record/railties/jdbcmysql_error.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -#FIXME Remove if ArJdbcMysql will give. -module ArJdbcMySQL #:nodoc: - class Error < StandardError #:nodoc: - attr_accessor :error_number, :sql_state - - def initialize(msg) - super - @error_number = nil - @sql_state = nil - end - - # Mysql gem compatibility - alias_method :errno, :error_number - alias_method :error, :message - end -end diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index d6a46f7743..e697fa6def 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -3,8 +3,6 @@ module ActiveRecord module Tasks # :nodoc: class MySQLDatabaseTasks # :nodoc: - ACCESS_DENIED_ERROR = 1045 - delegate :connection, :establish_connection, to: ActiveRecord::Base def initialize(configuration) @@ -21,18 +19,6 @@ module ActiveRecord else raise end - rescue error_class => error - if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR - $stdout.print error.message - establish_connection root_configuration_without_database - connection.create_database configuration["database"], creation_options - if configuration["username"] != "root" - connection.execute grant_statement.gsub(/\s+/, " ").strip - end - establish_connection configuration - else - raise - end end def drop @@ -97,37 +83,6 @@ module ActiveRecord end end - def error_class - if configuration["adapter"].include?("jdbc") - require "active_record/railties/jdbcmysql_error" - ArJdbcMySQL::Error - elsif defined?(Mysql2) - Mysql2::Error - else - StandardError - end - end - - def grant_statement - <<-SQL -GRANT ALL PRIVILEGES ON `#{configuration['database']}`.* - TO '#{configuration['username']}'@'localhost' -IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION; - SQL - end - - def root_configuration_without_database - configuration_without_database.merge( - "username" => "root", - "password" => root_password - ) - end - - def root_password - $stdout.print "Please provide the root password for your MySQL installation\n>" - $stdin.gets.strip - end - def prepare_command_options args = { "host" => "--host", diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index 6c9b0040ad..047153e7cc 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -75,7 +75,7 @@ if current_adapter?(:Mysql2Adapter) end end - class MysqlDBCreateAsRootTest < ActiveRecord::TestCase + class MysqlDBCreateWithInvalidPermissionsTest < ActiveRecord::TestCase def setup @connection = stub("Connection", create_database: true) @error = Mysql2::Error.new("Invalid permissions") @@ -86,13 +86,8 @@ if current_adapter?(:Mysql2Adapter) "password" => "wossname" } - $stdin.stubs(:gets).returns("secret\n") - $stdout.stubs(:print).returns(nil) - @error.stubs(:errno).returns(1045) ActiveRecord::Base.stubs(:connection).returns(@connection) - ActiveRecord::Base.stubs(:establish_connection). - raises(@error). - then.returns(true) + ActiveRecord::Base.stubs(:establish_connection).raises(@error) $stdout, @original_stdout = StringIO.new, $stdout $stderr, @original_stderr = StringIO.new, $stderr @@ -102,75 +97,11 @@ if current_adapter?(:Mysql2Adapter) $stdout, $stderr = @original_stdout, @original_stderr end - def test_root_password_is_requested - assert_permissions_granted_for("pat") - $stdin.expects(:gets).returns("secret\n") - - ActiveRecord::Tasks::DatabaseTasks.create @configuration - end - - def test_connection_established_as_root - assert_permissions_granted_for("pat") - ActiveRecord::Base.expects(:establish_connection).with( - "adapter" => "mysql2", - "database" => nil, - "username" => "root", - "password" => "secret" - ) - - ActiveRecord::Tasks::DatabaseTasks.create @configuration - end - - def test_database_created_by_root - assert_permissions_granted_for("pat") - @connection.expects(:create_database). - with("my-app-db", {}) - - ActiveRecord::Tasks::DatabaseTasks.create @configuration - end - - def test_grant_privileges_for_normal_user - assert_permissions_granted_for("pat") - ActiveRecord::Tasks::DatabaseTasks.create @configuration - end - - def test_do_not_grant_privileges_for_root_user - @configuration["username"] = "root" - @configuration["password"] = "" - ActiveRecord::Tasks::DatabaseTasks.create @configuration - end - - def test_connection_established_as_normal_user - assert_permissions_granted_for("pat") - ActiveRecord::Base.expects(:establish_connection).returns do - ActiveRecord::Base.expects(:establish_connection).with( - "adapter" => "mysql2", - "database" => "my-app-db", - "username" => "pat", - "password" => "secret" - ) - - raise @error - end - - ActiveRecord::Tasks::DatabaseTasks.create @configuration - end - - def test_raises_error_when_other_errors - @error.stubs(:errno).returns(42) - + def test_raises_error assert_raises(Mysql2::Error) do ActiveRecord::Tasks::DatabaseTasks.create @configuration end end - - private - - def assert_permissions_granted_for(db_user) - db_name = @configuration["database"] - db_password = @configuration["password"] - @connection.expects(:execute).with("GRANT ALL PRIVILEGES ON `#{db_name}`.* TO '#{db_user}'@'localhost' IDENTIFIED BY '#{db_password}' WITH GRANT OPTION;") - end end class MySQLDBDropTest < ActiveRecord::TestCase -- cgit v1.2.3 From 952eb506ebbfac24665a3a3429d1db9663cbb77a Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Tue, 31 Oct 2017 08:47:58 +0900 Subject: Remove unused `MissingRequestError` `MissingRequestError` is no longer used since 1e2b0ce. --- actionview/lib/action_view.rb | 1 - actionview/lib/action_view/template/error.rb | 3 --- 2 files changed, 4 deletions(-) diff --git a/actionview/lib/action_view.rb b/actionview/lib/action_view.rb index 2069ea042a..9533f8d56c 100644 --- a/actionview/lib/action_view.rb +++ b/actionview/lib/action_view.rb @@ -76,7 +76,6 @@ module ActionView autoload :MissingTemplate autoload :ActionViewError autoload :EncodingError - autoload :MissingRequestError autoload :TemplateError autoload :WrongEncodingError end diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb index 2b0b25817b..4e3c02e05e 100644 --- a/actionview/lib/action_view/template/error.rb +++ b/actionview/lib/action_view/template/error.rb @@ -10,9 +10,6 @@ module ActionView class EncodingError < StandardError #:nodoc: end - class MissingRequestError < StandardError #:nodoc: - end - class WrongEncodingError < EncodingError #:nodoc: def initialize(string, encoding) @string, @encoding = string, encoding -- cgit v1.2.3 From 19610f7b3abbc6f5a8d18472104d4799a9795336 Mon Sep 17 00:00:00 2001 From: Dave Gynn Date: Tue, 31 Oct 2017 02:13:53 -0700 Subject: Prevent source line wrapping in rescue layout Long source lines cause line wrapping in the extracted source section of the rescue handler page which can make the line numbers not match up with the source lines. --- actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb index e0509f56f4..39ea25bdfc 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb @@ -106,6 +106,7 @@ .line { padding-left: 10px; + white-space: pre; } .line:hover { -- cgit v1.2.3 From 6ae64c13fd22cd9e5fed02235c056123193611dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Fran=C3=A7a?= Date: Tue, 31 Oct 2017 13:53:37 -0400 Subject: Fix grammar issue [ci skip] Closes #31018 --- RELEASING_RAILS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING_RAILS.md b/RELEASING_RAILS.md index 3ff28c29f5..c61fe7eb13 100644 --- a/RELEASING_RAILS.md +++ b/RELEASING_RAILS.md @@ -141,7 +141,7 @@ lists where you should announce: Use Markdown format for your announcement. Remember to ask people to report issues with the release candidate to the rails-core mailing list. -NOTE: For patch releases there's a `rake announce` task to generate the release +NOTE: For patch releases, there's a `rake announce` task to generate the release post. It supports multiple patch releases too: ``` -- cgit v1.2.3 From 82e973dfd523e6ad298a43840b55c2d0f16591ec Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Tue, 31 Oct 2017 18:06:23 +0000 Subject: Address incorrect number of queries executed at Oracle enhanced adapter This pull request addresses these 17 failures when tested with Oracle enhanced adapter. All of these failures are due to the incorrect number of queries. Here is the first one. ```ruby $ ARCONN=oracle bin/test test/cases/nested_attributes_test.rb:1086 Using oracle Run options: --seed 27985 F Finished in 0.874514s, 1.1435 runs/s, 1.1435 assertions/s. 1) Failure: TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations#test_circular_references_do_not_perform_unnecessary_queries [/home/yahonda/git/rails/activerecord/test/cases/nested_attributes_test.rb:1086]: 6 instead of 3 queries were executed. Queries: select us.sequence_name from all_sequences us where us.sequence_owner = :owner and us.sequence_name = :sequence_name INSERT INTO "SHIPS" ("NAME", "ID") VALUES (:a1, :a2) select us.sequence_name from all_sequences us where us.sequence_owner = :owner and us.sequence_name = :sequence_name INSERT INTO "SHIP_PARTS" ("NAME", "SHIP_ID", "UPDATED_AT", "ID") VALUES (:a1, :a2, :a3, :a4) select us.sequence_name from all_sequences us where us.sequence_owner = :owner and us.sequence_name = :sequence_name INSERT INTO "TREASURES" ("LOOTER_ID", "LOOTER_TYPE", "SHIP_ID", "ID") VALUES (:a1, :a2, :a3, :a4). Expected: 3 Actual: 6 1 runs, 1 assertions, 1 failures, 0 errors, 0 skips $ ``` Since https://github.com/rsim/oracle-enhanced/pull/1490 Oracle enhanced adapter drops its own schema caching called OracleEnhancedAdapter.cache_columns` to use Rails scehema cache generated by `db:schema:cache:dump`. By this change some extra sql statements executed at ActiveRecord unit test, which can be fixed by adding the sql statement to `oracle_ignored`. * All 17 failures fixed by this pull request: ```ruby ARCONN=oracle bin/test test/cases/nested_attributes_test.rb:1086 ARCONN=oracle bin/test test/cases/locking_test.rb:308 ARCONN=oracle bin/test test/cases/locking_test.rb:365 ARCONN=oracle bin/test test/cases/dirty_test.rb:351 ARCONN=oracle bin/test test/cases/dirty_test.rb:334 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:192 ARCONN=oracle bin/test test/cases/associations/has_many_associations_test.rb:950 ARCONN=oracle bin/test test/cases/associations/has_many_associations_test.rb:1059 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:627 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:607 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:617 ARCONN=oracle bin/test test/cases/autosave_association_test.rb:641 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:546 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:297 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:586 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:172 ARCONN=oracle bin/test test/cases/associations/has_many_through_associations_test.rb:269 ``` --- activerecord/test/cases/test_case.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index e57ebf56c8..06a8693a7d 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -110,7 +110,7 @@ module ActiveRecord # FIXME: this needs to be refactored so specific database can add their own # ignored SQL, or better yet, use a different notification for the queries # instead examining the SQL content. - oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im] + oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im, /^\s*select .* from all_sequences/im] mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im] postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im] -- cgit v1.2.3 From 146b1c2e3389bc70ea0b54abf7843fc1d6c8cd5f Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 1 Nov 2017 07:32:04 +0900 Subject: Enable `Style/RedundantReturn` rubocop rule, and fixed a couple more Follow up of #31004. --- .rubocop.yml | 4 ++++ actioncable/lib/action_cable/connection/client_socket.rb | 2 +- .../test/cases/adapters/mysql2/active_schema_test.rb | 2 +- .../lib/active_support/core_ext/numeric/conversions.rb | 14 +++++++------- guides/rails_guides/levenshtein.rb | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index ec33b1ee33..399fc66730 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -136,6 +136,10 @@ Lint/EndAlignment: Lint/RequireParentheses: Enabled: true +Style/RedundantReturn: + Enabled: true + AllowMultipleReturnValues: true + Style/Semicolon: Enabled: true AllowAsExpressionSeparator: true diff --git a/actioncable/lib/action_cable/connection/client_socket.rb b/actioncable/lib/action_cable/connection/client_socket.rb index ba33c8b982..10289ab55c 100644 --- a/actioncable/lib/action_cable/connection/client_socket.rb +++ b/actioncable/lib/action_cable/connection/client_socket.rb @@ -21,7 +21,7 @@ module ActionCable return true if env["HTTP_X_FORWARDED_PROTO"] == "https" return true if env["rack.url_scheme"] == "https" - return false + false end CONNECTING = 0 diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index a6b83ec377..4e73c557ed 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -9,7 +9,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase def setup ActiveRecord::Base.connection.singleton_class.class_eval do alias_method :execute_without_stub, :execute - def execute(sql, name = nil) return sql end + def execute(sql, name = nil) sql end end end diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb index e675f32cd4..f6c2713986 100644 --- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb +++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb @@ -108,19 +108,19 @@ module ActiveSupport::NumericWithFormat when Integer, String super(format) when :phone - return ActiveSupport::NumberHelper.number_to_phone(self, options || {}) + ActiveSupport::NumberHelper.number_to_phone(self, options || {}) when :currency - return ActiveSupport::NumberHelper.number_to_currency(self, options || {}) + ActiveSupport::NumberHelper.number_to_currency(self, options || {}) when :percentage - return ActiveSupport::NumberHelper.number_to_percentage(self, options || {}) + ActiveSupport::NumberHelper.number_to_percentage(self, options || {}) when :delimited - return ActiveSupport::NumberHelper.number_to_delimited(self, options || {}) + ActiveSupport::NumberHelper.number_to_delimited(self, options || {}) when :rounded - return ActiveSupport::NumberHelper.number_to_rounded(self, options || {}) + ActiveSupport::NumberHelper.number_to_rounded(self, options || {}) when :human - return ActiveSupport::NumberHelper.number_to_human(self, options || {}) + ActiveSupport::NumberHelper.number_to_human(self, options || {}) when :human_size - return ActiveSupport::NumberHelper.number_to_human_size(self, options || {}) + ActiveSupport::NumberHelper.number_to_human_size(self, options || {}) when Symbol super() else diff --git a/guides/rails_guides/levenshtein.rb b/guides/rails_guides/levenshtein.rb index bafa6bfe9d..c48af797fa 100644 --- a/guides/rails_guides/levenshtein.rb +++ b/guides/rails_guides/levenshtein.rb @@ -38,7 +38,7 @@ module RailsGuides d[m] = x end - return x + x end end end -- cgit v1.2.3 From 7fe50a714623b53a0c01e6c2b140df9c2b993f1d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 1 Nov 2017 09:29:50 +0900 Subject: Fix failing `bundle install` on CI https://travis-ci.org/rails/rails/jobs/295470534#L2133 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3301694415..c333828f64 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -404,7 +404,7 @@ GEM loofah (~> 2.0) rainbow (2.2.2) rake - rake (12.0.0) + rake (12.2.1) rb-fsevent (0.10.2) rdoc (5.1.0) redcarpet (3.2.3) -- cgit v1.2.3 From d407e0821abaed642a7e5cb5ddb1f20a119dfa20 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 1 Nov 2017 09:25:36 +0900 Subject: Avoid bundler 1.16.0 for now It's causing bug report template tests to fail. https://travis-ci.org/rails/rails/jobs/295520851 This seems an issue of bundler. Ref: https://github.com/bundler/bundler/issues/6072 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 851365acbd..e357a0ca3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ bundler_args: --without test --jobs 3 --retry 3 before_install: - "rm ${BUNDLE_GEMFILE}.lock" - "travis_retry gem update --system" - - "travis_retry gem update bundler" + - "travis_retry gem install bundler -v 1.15.4" - "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/kr/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)" - "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd" - "[[ -z $encrypted_8a915ebdd931_key && -z $encrypted_8a915ebdd931_iv ]] || openssl aes-256-cbc -K $encrypted_8a915ebdd931_key -iv $encrypted_8a915ebdd931_iv -in activestorage/test/service/configurations.yml.enc -out activestorage/test/service/configurations.yml -d" -- cgit v1.2.3 From 6a55fbc858a87f454e0381e41fa1f98402615d2a Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 1 Nov 2017 10:31:15 +0900 Subject: Remove bundler 1.16.0 Since 1.16.0 is installed by default, it seems that the newer one will be used even if specify an older version. Ref: https://travis-ci.org/rails/rails/jobs/295553738#L1718 Follow up of #31023 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e357a0ca3e..1a3890b7d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ bundler_args: --without test --jobs 3 --retry 3 before_install: - "rm ${BUNDLE_GEMFILE}.lock" - "travis_retry gem update --system" + - "rvm @global do gem uninstall bundler --all --ignore-dependencies --executables" - "travis_retry gem install bundler -v 1.15.4" - "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/kr/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)" - "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd" -- cgit v1.2.3 From 14b7d673a3efb676ce29f483ded84214aae6d745 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 1 Nov 2017 02:40:34 +0000 Subject: Workaround for ActionMailer failures by not installing mail 2.7 ```ruby TestHelperMailerTest#test_encode BaseTest#test_implicit_multipart_with_attachments_creates_nested_parts BaseTest#test_implicit_multipart_with_attachments_and_sort_order BaseTest#test_explicit_multipart_with_attachments_creates_nested_parts ``` Refer https://travis-ci.org/rails/rails/jobs/295571582 --- Gemfile | 3 +++ Gemfile.lock | 1 + 2 files changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 2704e1e806..1a1b1584e8 100644 --- a/Gemfile +++ b/Gemfile @@ -168,3 +168,6 @@ end gem "ibm_db" if ENV["IBM_DB"] gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "wdm", ">= 0.1.0", platforms: [:mingw, :mswin, :x64_mingw, :mswin64] + +# Workaround not to install mail 2.7 +gem "mail", ">= 2.5.4", "< 2.7" diff --git a/Gemfile.lock b/Gemfile.lock index c333828f64..c9dd54d92a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -545,6 +545,7 @@ DEPENDENCIES kindlerb (~> 1.2.0) libxml-ruby listen (>= 3.0.5, < 3.2) + mail (>= 2.5.4, < 2.7) mini_magick minitest-bisect mocha -- cgit v1.2.3 From 0931e17ebf621e5518bc0546ea13268420f989ee Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Tue, 31 Oct 2017 21:44:55 -0700 Subject: Fix tests on Mail 2.7 Reverts 4d96be1c27bd6faed957b197a461f18543acebf2 References #31026 --- Gemfile | 3 --- Gemfile.lock | 5 ++--- actionmailer/test/base_test.rb | 31 +++++++++++++++---------------- actionmailer/test/test_helper_test.rb | 2 +- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Gemfile b/Gemfile index 1a1b1584e8..2704e1e806 100644 --- a/Gemfile +++ b/Gemfile @@ -168,6 +168,3 @@ end gem "ibm_db" if ENV["IBM_DB"] gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "wdm", ">= 0.1.0", platforms: [:mingw, :mswin, :x64_mingw, :mswin64] - -# Workaround not to install mail 2.7 -gem "mail", ">= 2.5.4", "< 2.7" diff --git a/Gemfile.lock b/Gemfile.lock index c9dd54d92a..cc3b0508c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -333,8 +333,8 @@ GEM multi_json (~> 1.10) loofah (2.0.3) nokogiri (>= 1.5.9) - mail (2.6.6) - mime-types (>= 1.16, < 4) + mail (2.7.0) + mini_mime (>= 0.1.1) memoist (0.16.0) metaclass (0.0.4) method_source (0.8.2) @@ -545,7 +545,6 @@ DEPENDENCIES kindlerb (~> 1.2.0) libxml-ruby listen (>= 3.0.5, < 3.2) - mail (>= 2.5.4, < 2.7) mini_magick minitest-bisect mocha diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 0da969c349..977e0e201e 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -322,22 +322,21 @@ class BaseTest < ActiveSupport::TestCase test "implicit multipart with attachments creates nested parts" do email = BaseMailer.implicit_multipart(attachments: true) - assert_equal("application/pdf", email.parts[0].mime_type) - assert_equal("multipart/alternative", email.parts[1].mime_type) - assert_equal("text/plain", email.parts[1].parts[0].mime_type) - assert_equal("TEXT Implicit Multipart", email.parts[1].parts[0].body.encoded) - assert_equal("text/html", email.parts[1].parts[1].mime_type) - assert_equal("HTML Implicit Multipart", email.parts[1].parts[1].body.encoded) + assert_equal(%w[ application/pdf multipart/alternative ], email.parts.map(&:mime_type).sort) + multipart = email.parts.detect { |p| p.mime_type == "multipart/alternative" } + assert_equal("text/plain", multipart.parts[0].mime_type) + assert_equal("TEXT Implicit Multipart", multipart.parts[0].body.encoded) + assert_equal("text/html", multipart.parts[1].mime_type) + assert_equal("HTML Implicit Multipart", multipart.parts[1].body.encoded) end test "implicit multipart with attachments and sort order" do order = ["text/html", "text/plain"] with_default BaseMailer, parts_order: order do email = BaseMailer.implicit_multipart(attachments: true) - assert_equal("application/pdf", email.parts[0].mime_type) - assert_equal("multipart/alternative", email.parts[1].mime_type) - assert_equal("text/plain", email.parts[1].parts[1].mime_type) - assert_equal("text/html", email.parts[1].parts[0].mime_type) + assert_equal(%w[ application/pdf multipart/alternative ], email.parts.map(&:mime_type).sort) + multipart = email.parts.detect { |p| p.mime_type == "multipart/alternative" } + assert_equal(%w[ text/html text/plain ], multipart.parts.map(&:mime_type).sort) end end @@ -427,12 +426,12 @@ class BaseTest < ActiveSupport::TestCase test "explicit multipart with attachments creates nested parts" do email = BaseMailer.explicit_multipart(attachments: true) - assert_equal("application/pdf", email.parts[0].mime_type) - assert_equal("multipart/alternative", email.parts[1].mime_type) - assert_equal("text/plain", email.parts[1].parts[0].mime_type) - assert_equal("TEXT Explicit Multipart", email.parts[1].parts[0].body.encoded) - assert_equal("text/html", email.parts[1].parts[1].mime_type) - assert_equal("HTML Explicit Multipart", email.parts[1].parts[1].body.encoded) + assert_equal(%w[ application/pdf multipart/alternative ], email.parts.map(&:mime_type).sort) + multipart = email.parts.detect { |p| p.mime_type == "multipart/alternative" } + assert_equal("text/plain", multipart.parts[0].mime_type) + assert_equal("TEXT Explicit Multipart", multipart.parts[0].body.encoded) + assert_equal("text/html", multipart.parts[1].mime_type) + assert_equal("HTML Explicit Multipart", multipart.parts[1].body.encoded) end test "explicit multipart with templates" do diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb index 5470d51599..3866097389 100644 --- a/actionmailer/test/test_helper_test.rb +++ b/actionmailer/test/test_helper_test.rb @@ -54,7 +54,7 @@ class TestHelperMailerTest < ActionMailer::TestCase end def test_encode - assert_equal "=?UTF-8?Q?This_is_=E3=81=82_string?=", encode("This is あ string") + assert_equal "This is あ string", Mail::Encodings.q_value_decode(encode("This is あ string")) end def test_read_fixture -- cgit v1.2.3 From 23d5d5830690a8d22f0fe3762a8399a1f8a6d069 Mon Sep 17 00:00:00 2001 From: Aditya Kapoor Date: Wed, 1 Nov 2017 12:00:17 +0530 Subject: [ci skip] show the correct example to demonstrate inflections. --- activesupport/lib/active_support/inflector/transliterate.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb index 9801f1d118..e689f0718e 100644 --- a/activesupport/lib/active_support/inflector/transliterate.rb +++ b/activesupport/lib/active_support/inflector/transliterate.rb @@ -76,13 +76,19 @@ module ActiveSupport # To use a custom separator, override the `separator` argument. # # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth" - # parameterize("^trés|Jolie-- ", separator: '_') # => "tres_jolie" + # parameterize("^trés|Jolie__ ", separator: '_') # => "tres_jolie" # # To preserve the case of the characters in a string, use the `preserve_case` argument. # # parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth" # parameterize("^trés|Jolie-- ", preserve_case: true) # => "tres-Jolie" # + # It preserves dashes and underscores unless they are used as separators: + # + # parameterize("^trés|Jolie__ ") # => "tres-jolie__" + # parameterize("^trés|Jolie-- ", separator: "_") # => "tres_jolie--" + # parameterize("^trés_Jolie-- ", separator: ".") # => "tres_jolie--" + # def parameterize(string, separator: "-", preserve_case: false) # Replace accented chars with their ASCII equivalents. parameterized_string = transliterate(string) -- cgit v1.2.3 From 1ede34697539d5094090c7c8e3ab95359f706904 Mon Sep 17 00:00:00 2001 From: Pierre Hedkvist Date: Wed, 1 Nov 2017 10:27:29 +0000 Subject: Fixed typo in test for activesupport parameterize --- activesupport/test/core_ext/string_ext_test.rb | 2 +- activesupport/test/inflector_test_cases.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 9fd6d8ac0f..5c5abe9fd1 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -197,7 +197,7 @@ class StringInflectionsTest < ActiveSupport::TestCase end def test_string_parameterized_underscore_preserve_case - StringToParameterizePreserceCaseWithUnderscore.each do |normal, slugged| + StringToParameterizePreserveCaseWithUnderscore.each do |normal, slugged| assert_equal(slugged, normal.parameterize(separator: "_", preserve_case: true)) end end diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index f1214671ce..689370cccf 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -221,7 +221,7 @@ module InflectorTestCases "Test with malformed utf8 \251" => "test_with_malformed_utf8" } - StringToParameterizePreserceCaseWithUnderscore = { + StringToParameterizePreserveCaseWithUnderscore = { "Donald E. Knuth" => "Donald_E_Knuth", "Random text with *(bad)* characters" => "Random_text_with_bad_characters", "With-some-dashes" => "With-some-dashes", -- cgit v1.2.3 From a235b9f9d1656b6f47d1100f2fd07130ddaf1fa1 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 1 Nov 2017 11:46:10 +0100 Subject: updates autoloading guide for Ruby 2.5 [ci skip] --- guides/source/autoloading_and_reloading_constants.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md index ede0324a51..dea87a18f8 100644 --- a/guides/source/autoloading_and_reloading_constants.md +++ b/guides/source/autoloading_and_reloading_constants.md @@ -330,11 +330,17 @@ its resolution next. Let's define *parent* to be that qualifying class or module object, that is, `Billing` in the example above. The algorithm for qualified constants goes like this: -1. The constant is looked up in the parent and its ancestors. +1. The constant is looked up in the parent and its ancestors. In Ruby >= 2.5, +`Object` is skipped if present among the ancestors. `Kernel` and `BasicObject` +are still checked though. 2. If the lookup fails, `const_missing` is invoked in the parent. The default implementation of `const_missing` raises `NameError`, but it can be overridden. +INFO. In Ruby < 2.5 `String::Hash` evaluates to `Hash` and the interpreter +issues a warning: "toplevel constant Hash referenced by String::Hash". Starting +with 2.5, `String::Hash` raises `NameError` because `Object` is skipped. + As you see, this algorithm is simpler than the one for relative constants. In particular, the nesting plays no role here, and modules are not special-cased, if neither they nor their ancestors have the constants, `Object` is **not** @@ -1178,6 +1184,8 @@ end #### Qualified References +WARNING. This gotcha is only possible in Ruby < 2.5. + Given ```ruby -- cgit v1.2.3 From 1944520627f59823ba98d5cf24f56591b622d670 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 1 Nov 2017 13:28:52 +0000 Subject: Ignore "gem "bundler" cannot be uninstalled because it is a default gem" error when tested with ruby-head This pull request attempts to ignore the following error when tested with ruby-head which has bundler as a default gem. ```ruby $ rvm @global do gem uninstall bundler --all --ignore-dependencies --executables ERROR: While executing gem ... (Gem::InstallError) gem "bundler" cannot be uninstalled because it is a default gem The command "rvm @global do gem uninstall bundler --all --ignore-dependencies --executables" failed and exited with 1 during . ``` Refer https://travis-ci.org/rails/rails/jobs/295600391 This workaround should be removed once https://github.com/bundler/bundler/issues/6072 is addressed. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1a3890b7d9..ffb2e33282 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ bundler_args: --without test --jobs 3 --retry 3 before_install: - "rm ${BUNDLE_GEMFILE}.lock" - "travis_retry gem update --system" - - "rvm @global do gem uninstall bundler --all --ignore-dependencies --executables" + - "rvm @global do gem uninstall bundler --all --ignore-dependencies --executables || true" - "travis_retry gem install bundler -v 1.15.4" - "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/kr/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)" - "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd" -- cgit v1.2.3 From d5defda77f3d941f238c35eae12146d5d4737e54 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 2 Nov 2017 09:58:54 +0900 Subject: Fix "warning: instance variable @defined_root not initialized" Currently, the following error is shows only when run the test using `bin/test`. ``` ./bin/test -w test/template/log_subscriber_test.rb Run options: --seed 17167 # Running: /rails/actionview/test/template/log_subscriber_test.rb:34: warning: instance variable @defined_root not initialized ``` In `AVLogSubscriberTest`, if the `Rails.root` is not defined, define the method and undef it in teardown. https://github.com/rails/rails/blob/master/actionview/test/template/log_subscriber_test.rb#L21..L33 However, in `bin/test`, `Rails.root` is defined, which results in referring to uninitialized variables and warnings. --- actionview/test/template/log_subscriber_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionview/test/template/log_subscriber_test.rb b/actionview/test/template/log_subscriber_test.rb index a4d89ba0d1..7f4fd25573 100644 --- a/actionview/test/template/log_subscriber_test.rb +++ b/actionview/test/template/log_subscriber_test.rb @@ -30,7 +30,7 @@ class AVLogSubscriberTest < ActiveSupport::TestCase ActiveSupport::LogSubscriber.log_subscribers.clear # We need to undef `root`, RenderTestCases don't want this to be defined - Rails.instance_eval { undef :root } if @defined_root + Rails.instance_eval { undef :root } if defined?(@defined_root) end def set_logger(logger) -- cgit v1.2.3 From b0c9f0c8deb4a224609f5673dc850599e126535e Mon Sep 17 00:00:00 2001 From: haneru Date: Fri, 3 Nov 2017 00:38:44 +0900 Subject: Edited comment from request.rb --- actionpack/lib/action_dispatch/http/request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 60aa1d4e8a..d631281e4b 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -204,7 +204,7 @@ module ActionDispatch # # If the env contains +rack.early_hints+ then the server accepts HTTP2 push for Link headers. # - # The +send_early_hints+ method accepts an hash of links as follows: + # The +send_early_hints+ method accepts a hash of links as follows: # # send_early_hints("Link" => "; rel=preload; as=style\n; rel=preload") # -- cgit v1.2.3 From aa6bcbbac8517d5b077f21073e9902637d7c7157 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Thu, 2 Nov 2017 15:07:04 -0400 Subject: Allow third-party previewers/analyzers to customize their tempdirs --- activestorage/lib/active_storage/downloading.rb | 9 +++++++-- activestorage/lib/active_storage/previewer.rb | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/activestorage/lib/active_storage/downloading.rb b/activestorage/lib/active_storage/downloading.rb index ceb7cce0c7..3dac6b116a 100644 --- a/activestorage/lib/active_storage/downloading.rb +++ b/activestorage/lib/active_storage/downloading.rb @@ -3,9 +3,9 @@ module ActiveStorage module Downloading private - # Opens a new tempfile and copies blob data into it. Yields the tempfile. + # Opens a new tempfile in #tempdir and copies blob data into it. Yields the tempfile. def download_blob_to_tempfile # :doc: - Tempfile.open("ActiveStorage") do |file| + Tempfile.open("ActiveStorage", tempdir) do |file| download_blob_to file yield file end @@ -17,5 +17,10 @@ module ActiveStorage blob.download { |chunk| file.write(chunk) } file.rewind end + + # Returns the directory in which tempfiles should be opened. Defaults to +Dir.tmpdir+. + def tempdir # :doc: + Dir.tmpdir + end end end diff --git a/activestorage/lib/active_storage/previewer.rb b/activestorage/lib/active_storage/previewer.rb index 460f6d5678..ed75bae3b5 100644 --- a/activestorage/lib/active_storage/previewer.rb +++ b/activestorage/lib/active_storage/previewer.rb @@ -40,8 +40,10 @@ module ActiveStorage # end # end # end + # + # The output tempfile is opened in the directory returned by ActiveStorage::Downloading#tempdir. def draw(*argv) # :doc: - Tempfile.open("ActiveStorage") do |file| + Tempfile.open("ActiveStorage", tempdir) do |file| capture(*argv, to: file) yield file end -- cgit v1.2.3 From d404e2cbe9e078d286c7acb13fd18fb3c04a3f4f Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Thu, 2 Nov 2017 14:50:38 -0700 Subject: Use blank? check instead of key? check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to prevent an extra string allocation when there is a rel argument and performs better/within error of the key check for other scenarios such as passing in rel: nil ```ruby begin require "bundler/inline" rescue LoadError => e $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler" raise e end gemfile(true) do source "https://rubygems.org" gem "benchmark-ips" gem "rails" end def allocate_count GC.disable before = ObjectSpace.count_objects yield after = ObjectSpace.count_objects after.each { |k,v| after[k] = v - before[k] } after[:T_HASH] -= 1 # probe effect - we created the before hash. GC.enable result = after.reject { |k,v| v == 0 } GC.start result end @hash = {} def master_version "#{@hash["rel"]} nofollow".lstrip end def key_version if @hash.key?("rel") "#{@hash["rel"]} nofollow".lstrip else "nofollow" end end def present_version if @hash["rel"].present? "#{@hash["rel"]} nofollow" else "nofollow".freeze end end def nil_version if @hash["rel"].nil? "nofollow".freeze else "#{@hash["rel"]} nofollow" end end def blank_version if @hash["rel"].blank? "nofollow".freeze else "#{@hash["rel"]} nofollow" end end def test puts "master_version" puts allocate_count { 1000.times { master_version } } puts "key_version" puts allocate_count { 1000.times { key_version } } puts "present_version" puts allocate_count { 1000.times { present_version } } puts "nil_version" puts allocate_count { 1000.times { nil_version } } puts "blank_version" puts allocate_count { 1000.times { blank_version } } Benchmark.ips do |x| x.report("master_version") { master_version } x.report("key_version") { key_version } x.report("present_version") { present_version } x.report("nil_version") { nil_version } x.report("blank_version") { blank_version } x.compare! end end puts 'no rel key' test puts 'rel key with real stuff' @hash['rel'] = 'hi'.freeze test puts 'rel key with nil' @hash['rel'] = nil test puts 'rel key with ""' @hash['rel'] = "" test ``` ``` no rel key master_version {:FREE=>-2818, :T_STRING=>3052} key_version {:FREE=>-1} present_version {:FREE=>-1} nil_version {:FREE=>-1} blank_version {:FREE=>-1} Warming up -------------------------------------- master_version 124.677k i/100ms key_version 227.992k i/100ms present_version 208.068k i/100ms nil_version 235.272k i/100ms blank_version 176.274k i/100ms Calculating ------------------------------------- master_version 1.968M (±10.8%) i/s - 9.725M in 5.010763s key_version 7.734M (±11.2%) i/s - 38.075M in 5.001613s present_version 5.688M (±11.4%) i/s - 28.089M in 5.019560s nil_version 6.965M (±10.2%) i/s - 34.585M in 5.024806s blank_version 6.139M (±18.7%) i/s - 29.085M in 5.010919s Comparison: key_version: 7734058.3 i/s nil_version: 6965050.2 i/s - same-ish: difference falls within error blank_version: 6138744.3 i/s - same-ish: difference falls within error present_version: 5688248.4 i/s - 1.36x slower master_version: 1967932.3 i/s - 3.93x slower rel key with real stuff master_version {:FREE=>-2001, :T_STRING=>2000} key_version {:FREE=>-2001, :T_STRING=>2000} present_version {:FREE=>-1001, :T_STRING=>1000} nil_version {:FREE=>-1002, :T_STRING=>1000, :T_IMEMO=>1} blank_version {:FREE=>-1001, :T_STRING=>1000} Warming up -------------------------------------- master_version 93.351k i/100ms key_version 89.747k i/100ms present_version 91.963k i/100ms nil_version 103.370k i/100ms blank_version 74.845k i/100ms Calculating ------------------------------------- master_version 2.179M (±21.4%) i/s - 10.362M in 5.044668s key_version 2.345M (± 9.8%) i/s - 11.667M in 5.030982s present_version 1.738M (±14.8%) i/s - 8.553M in 5.056406s nil_version 2.485M (±19.1%) i/s - 11.888M in 5.015940s blank_version 1.951M (±12.3%) i/s - 9.580M in 5.011932s Comparison: nil_version: 2484704.1 i/s key_version: 2344664.8 i/s - same-ish: difference falls within error master_version: 2178975.8 i/s - same-ish: difference falls within error blank_version: 1950532.0 i/s - same-ish: difference falls within error present_version: 1737866.7 i/s - 1.43x slower rel key with nil master_version {:FREE=>-3001, :T_STRING=>3000} key_version {:FREE=>-3001, :T_STRING=>3000} present_version {:FREE=>-1} nil_version {:FREE=>-1} blank_version {:FREE=>-1} Warming up -------------------------------------- master_version 112.655k i/100ms key_version 105.048k i/100ms present_version 136.219k i/100ms nil_version 192.026k i/100ms blank_version 184.846k i/100ms Calculating ------------------------------------- master_version 1.893M (±12.6%) i/s - 9.238M in 5.002621s key_version 1.672M (±13.5%) i/s - 8.194M in 5.021197s present_version 4.484M (±20.5%) i/s - 21.114M in 5.002982s nil_version 5.294M (±18.1%) i/s - 25.155M in 5.020721s blank_version 5.588M (± 6.7%) i/s - 27.912M in 5.019305s Comparison: blank_version: 5588489.6 i/s nil_version: 5293929.9 i/s - same-ish: difference falls within error present_version: 4484493.7 i/s - same-ish: difference falls within error master_version: 1892919.0 i/s - 2.95x slower key_version: 1672343.9 i/s - 3.34x slower rel key with "" master_version {:FREE=>-2001, :T_STRING=>2000} key_version {:FREE=>-2001, :T_STRING=>2000} present_version {:FREE=>-1} nil_version {:FREE=>-1001, :T_STRING=>1000} blank_version {:FREE=>-1} Warming up -------------------------------------- master_version 140.499k i/100ms key_version 124.738k i/100ms present_version 186.659k i/100ms nil_version 148.063k i/100ms blank_version 178.707k i/100ms Calculating ------------------------------------- master_version 1.826M (±24.2%) i/s - 8.289M in 5.026603s key_version 1.561M (±15.3%) i/s - 7.609M in 5.005662s present_version 3.622M (±19.9%) i/s - 17.173M in 5.042217s nil_version 2.438M (±11.5%) i/s - 12.141M in 5.053335s blank_version 4.911M (±15.5%) i/s - 23.768M in 5.009106s Comparison: blank_version: 4910741.1 i/s present_version: 3622183.5 i/s - same-ish: difference falls within error nil_version: 2437606.2 i/s - 2.01x slower master_version: 1825652.2 i/s - 2.69x slower key_version: 1560530.5 i/s - 3.15x slower ``` --- actionview/lib/action_view/helpers/url_helper.rb | 6 +- test.rb | 110 +++++++++++++++++++++++ 2 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 test.rb diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 4e436433c4..71fed39351 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -590,10 +590,10 @@ module ActionView def add_method_to_attributes!(html_options, method) if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/ - if html_options.key?("rel") - html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip - else + if html_options.("rel").blank? html_options["rel"] = "nofollow" + else + html_options["rel"] = "#{html_options["rel"]} nofollow" end end html_options["data-method"] = method diff --git a/test.rb b/test.rb new file mode 100644 index 0000000000..3d5e508fa7 --- /dev/null +++ b/test.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true +begin + require "bundler/inline" +rescue LoadError => e + $stderr.puts "Bundler version 1.10 or later is required. Please update + your Bundler" + raise e +end + +gemfile(true) do + source "https://rubygems.org" + + gem "benchmark-ips" + gem "rails" +end + +def allocate_count + GC.disable + before = ObjectSpace.count_objects + yield + after = ObjectSpace.count_objects + after.each { |k,v| after[k] = v - before[k] } + after[:T_HASH] -= 1 # probe effect - we created the before hash. + GC.enable + result = after.reject { |k,v| v == 0 } + GC.start + result +end + +@hash = {} + +def master_version + "#{@hash["rel"]} nofollow".lstrip +end + +def key_version + if @hash.key?("rel") + "#{@hash["rel"]} nofollow".lstrip + else + "nofollow" + end +end + +def present_version + if @hash["rel"].present? + "#{@hash["rel"]} nofollow" + else + "nofollow".freeze + end +end + +def nil_version + if @hash["rel"].nil? + "nofollow".freeze + else + "#{@hash["rel"]} nofollow" + end +end + +def blank_version + if @hash["rel"].blank? + "nofollow".freeze + else + "#{@hash["rel"]} nofollow" + end +end + +def test + puts "master_version" + puts allocate_count { 1000.times { master_version } } + puts "key_version" + puts allocate_count { 1000.times { key_version } } + puts "present_version" + puts allocate_count { 1000.times { present_version } } + puts "nil_version" + puts allocate_count { 1000.times { nil_version } } + puts "blank_version" + puts allocate_count { 1000.times { blank_version } } + + Benchmark.ips do |x| + x.report("master_version") { master_version } + x.report("key_version") { key_version } + x.report("present_version") { present_version } + x.report("nil_version") { nil_version } + x.report("blank_version") { blank_version } + x.compare! + end +end + +puts 'no rel key' + +test + +puts 'rel key with real stuff' + +@hash['rel'] = 'hi'.freeze + +test + +puts 'rel key with nil' + +@hash['rel'] = nil + +test + +puts 'rel key with ""' + +@hash['rel'] = "" + +test -- cgit v1.2.3 From 019c8ae814d0e89af3da543a956f22a4db92c5a3 Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Thu, 2 Nov 2017 14:55:51 -0700 Subject: Remove test file --- test.rb | 110 ---------------------------------------------------------------- 1 file changed, 110 deletions(-) delete mode 100644 test.rb diff --git a/test.rb b/test.rb deleted file mode 100644 index 3d5e508fa7..0000000000 --- a/test.rb +++ /dev/null @@ -1,110 +0,0 @@ -# frozen_string_literal: true -begin - require "bundler/inline" -rescue LoadError => e - $stderr.puts "Bundler version 1.10 or later is required. Please update - your Bundler" - raise e -end - -gemfile(true) do - source "https://rubygems.org" - - gem "benchmark-ips" - gem "rails" -end - -def allocate_count - GC.disable - before = ObjectSpace.count_objects - yield - after = ObjectSpace.count_objects - after.each { |k,v| after[k] = v - before[k] } - after[:T_HASH] -= 1 # probe effect - we created the before hash. - GC.enable - result = after.reject { |k,v| v == 0 } - GC.start - result -end - -@hash = {} - -def master_version - "#{@hash["rel"]} nofollow".lstrip -end - -def key_version - if @hash.key?("rel") - "#{@hash["rel"]} nofollow".lstrip - else - "nofollow" - end -end - -def present_version - if @hash["rel"].present? - "#{@hash["rel"]} nofollow" - else - "nofollow".freeze - end -end - -def nil_version - if @hash["rel"].nil? - "nofollow".freeze - else - "#{@hash["rel"]} nofollow" - end -end - -def blank_version - if @hash["rel"].blank? - "nofollow".freeze - else - "#{@hash["rel"]} nofollow" - end -end - -def test - puts "master_version" - puts allocate_count { 1000.times { master_version } } - puts "key_version" - puts allocate_count { 1000.times { key_version } } - puts "present_version" - puts allocate_count { 1000.times { present_version } } - puts "nil_version" - puts allocate_count { 1000.times { nil_version } } - puts "blank_version" - puts allocate_count { 1000.times { blank_version } } - - Benchmark.ips do |x| - x.report("master_version") { master_version } - x.report("key_version") { key_version } - x.report("present_version") { present_version } - x.report("nil_version") { nil_version } - x.report("blank_version") { blank_version } - x.compare! - end -end - -puts 'no rel key' - -test - -puts 'rel key with real stuff' - -@hash['rel'] = 'hi'.freeze - -test - -puts 'rel key with nil' - -@hash['rel'] = nil - -test - -puts 'rel key with ""' - -@hash['rel'] = "" - -test -- cgit v1.2.3 From ec13ef75260f6d143e947b0e5176e35e4c1229c2 Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Thu, 2 Nov 2017 17:54:54 -0700 Subject: Fix typo --- actionview/lib/action_view/helpers/url_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 71fed39351..9900e0cd03 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -590,7 +590,7 @@ module ActionView def add_method_to_attributes!(html_options, method) if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/ - if html_options.("rel").blank? + if html_options["rel"].blank? html_options["rel"] = "nofollow" else html_options["rel"] = "#{html_options["rel"]} nofollow" -- cgit v1.2.3 From 0d7ab973e15b476ed42471395027e329f95c8951 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 3 Nov 2017 10:09:43 +0900 Subject: Remove unused classes * `HasManyThroughCantDissociateNewRecords` and `HasManyThroughCantAssociateNewRecords` are no longer used since f6b12c1. * `ReadOnlyAssociation` is no longer used since 0da426b. --- activerecord/lib/active_record/associations.rb | 30 -------------------------- 1 file changed, 30 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index bc7e288500..661605d3e5 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -140,26 +140,6 @@ module ActiveRecord class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection #:nodoc: end - class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc: - def initialize(owner = nil, reflection = nil) - if owner && reflection - super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.") - else - super("Cannot associate new records.") - end - end - end - - class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc: - def initialize(owner = nil, reflection = nil) - if owner && reflection - super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.") - else - super("Cannot dissociate new records.") - end - end - end - class ThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc: def initialize(owner = nil, reflection = nil) if owner && reflection @@ -189,16 +169,6 @@ module ActiveRecord end end - class ReadOnlyAssociation < ActiveRecordError #:nodoc: - def initialize(reflection = nil) - if reflection - super("Cannot add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.") - else - super("Read-only reflection error.") - end - end - end - # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations # (has_many, has_one) when there is at least 1 child associated instance. # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project -- cgit v1.2.3 From b106242f52272c4a5ced7a0e9d1dcb1b50542501 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 3 Nov 2017 15:24:45 +0900 Subject: Specify bundler version in template files We have already specified to install `bundler` 1.15.4 in `.travis.yml`. https://github.com/rails/rails/blob/master/.travis.yml#L31..L32 However, `bundler` 1.16.0 may be used in the test. https://travis-ci.org/rails/rails/jobs/296582467#L2208 The test failed due to this influence. In order to avoid this, specifying `bundler` version in bug report templates. --- guides/bug_report_templates/action_controller_master.rb | 2 ++ guides/bug_report_templates/active_job_master.rb | 2 ++ guides/bug_report_templates/active_record_master.rb | 2 ++ guides/bug_report_templates/active_record_migrations_master.rb | 2 ++ guides/bug_report_templates/benchmark.rb | 2 ++ guides/bug_report_templates/generic_master.rb | 2 ++ 6 files changed, 12 insertions(+) diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb index cf76de80d2..932d329943 100644 --- a/guides/bug_report_templates/action_controller_master.rb +++ b/guides/bug_report_templates/action_controller_master.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +gem "bundler", "< 1.16" + begin require "bundler/inline" rescue LoadError => e diff --git a/guides/bug_report_templates/active_job_master.rb b/guides/bug_report_templates/active_job_master.rb index ce480cbb52..36d9137b71 100644 --- a/guides/bug_report_templates/active_job_master.rb +++ b/guides/bug_report_templates/active_job_master.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +gem "bundler", "< 1.16" + begin require "bundler/inline" rescue LoadError => e diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb index 78411e2d57..b66deb36f3 100644 --- a/guides/bug_report_templates/active_record_master.rb +++ b/guides/bug_report_templates/active_record_master.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +gem "bundler", "< 1.16" + begin require "bundler/inline" rescue LoadError => e diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb index fce8d1d848..737ce66d7b 100644 --- a/guides/bug_report_templates/active_record_migrations_master.rb +++ b/guides/bug_report_templates/active_record_migrations_master.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +gem "bundler", "< 1.16" + begin require "bundler/inline" rescue LoadError => e diff --git a/guides/bug_report_templates/benchmark.rb b/guides/bug_report_templates/benchmark.rb index fb51273e3e..f5c88086a9 100644 --- a/guides/bug_report_templates/benchmark.rb +++ b/guides/bug_report_templates/benchmark.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +gem "bundler", "< 1.16" + begin require "bundler/inline" rescue LoadError => e diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb index 384c8b1833..240571ba9a 100644 --- a/guides/bug_report_templates/generic_master.rb +++ b/guides/bug_report_templates/generic_master.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +gem "bundler", "< 1.16" + begin require "bundler/inline" rescue LoadError => e -- cgit v1.2.3 From 288fbc7ff47b6aae0d5bab978ae16858a425f643 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 3 Nov 2017 23:41:35 +0900 Subject: Revert "Merge pull request #31025 from y-yagi/follow_up_31023_part2" This reverts commit 6f481e05bb24fe3589ef0f65e97a9b1fa66ae0f7, reversing changes made to 592f790b7693c0a32cd06d5e8201639923a734c5. In favor of #31039. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ffb2e33282..e357a0ca3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,6 @@ bundler_args: --without test --jobs 3 --retry 3 before_install: - "rm ${BUNDLE_GEMFILE}.lock" - "travis_retry gem update --system" - - "rvm @global do gem uninstall bundler --all --ignore-dependencies --executables || true" - "travis_retry gem install bundler -v 1.15.4" - "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/kr/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)" - "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd" -- cgit v1.2.3 From 9ec67362054e874ed905310a79b670941fa397af Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Fri, 3 Nov 2017 11:29:21 -0400 Subject: Permit configuring Active Storage's job queue --- activestorage/app/jobs/active_storage/analyze_job.rb | 2 +- activestorage/app/jobs/active_storage/base_job.rb | 5 +++++ activestorage/app/jobs/active_storage/purge_job.rb | 2 +- activestorage/lib/active_storage.rb | 1 + activestorage/lib/active_storage/engine.rb | 19 +++++-------------- 5 files changed, 13 insertions(+), 16 deletions(-) create mode 100644 activestorage/app/jobs/active_storage/base_job.rb diff --git a/activestorage/app/jobs/active_storage/analyze_job.rb b/activestorage/app/jobs/active_storage/analyze_job.rb index a11a73d030..2a952f9f74 100644 --- a/activestorage/app/jobs/active_storage/analyze_job.rb +++ b/activestorage/app/jobs/active_storage/analyze_job.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Provides asynchronous analysis of ActiveStorage::Blob records via ActiveStorage::Blob#analyze_later. -class ActiveStorage::AnalyzeJob < ActiveJob::Base +class ActiveStorage::AnalyzeJob < ActiveStorage::BaseJob def perform(blob) blob.analyze end diff --git a/activestorage/app/jobs/active_storage/base_job.rb b/activestorage/app/jobs/active_storage/base_job.rb new file mode 100644 index 0000000000..6caab42a2d --- /dev/null +++ b/activestorage/app/jobs/active_storage/base_job.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class ActiveStorage::BaseJob < ActiveJob::Base + queue_as { ActiveStorage.queue } +end diff --git a/activestorage/app/jobs/active_storage/purge_job.rb b/activestorage/app/jobs/active_storage/purge_job.rb index 188840f702..98874d2250 100644 --- a/activestorage/app/jobs/active_storage/purge_job.rb +++ b/activestorage/app/jobs/active_storage/purge_job.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Provides asynchronous purging of ActiveStorage::Blob records via ActiveStorage::Blob#purge_later. -class ActiveStorage::PurgeJob < ActiveJob::Base +class ActiveStorage::PurgeJob < ActiveStorage::BaseJob # FIXME: Limit this to a custom ActiveStorage error retry_on StandardError diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb index cfdb2a8acd..d1ff6b7032 100644 --- a/activestorage/lib/active_storage.rb +++ b/activestorage/lib/active_storage.rb @@ -38,6 +38,7 @@ module ActiveStorage mattr_accessor :logger mattr_accessor :verifier + mattr_accessor :queue mattr_accessor :previewers, default: [] mattr_accessor :analyzers, default: [] end diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb index a01a14cd83..6cf6635c4f 100644 --- a/activestorage/lib/active_storage/engine.rb +++ b/activestorage/lib/active_storage/engine.rb @@ -19,9 +19,12 @@ module ActiveStorage config.eager_load_namespaces << ActiveStorage - initializer "active_storage.logger" do + initializer "active_storage.configs" do config.after_initialize do |app| - ActiveStorage.logger = app.config.active_storage.logger || Rails.logger + ActiveStorage.logger = app.config.active_storage.logger || Rails.logger + ActiveStorage.queue = app.config.active_storage.queue + ActiveStorage.previewers = app.config.active_storage.previewers || [] + ActiveStorage.analyzers = app.config.active_storage.analyzers || [] end end @@ -65,17 +68,5 @@ module ActiveStorage end end end - - initializer "active_storage.previewers" do - config.after_initialize do |app| - ActiveStorage.previewers = app.config.active_storage.previewers || [] - end - end - - initializer "active_storage.analyzers" do - config.after_initialize do |app| - ActiveStorage.analyzers = app.config.active_storage.analyzers || [] - end - end end end -- cgit v1.2.3 From acdba1c6a653bf5c787d3457af95b37708be1e2b Mon Sep 17 00:00:00 2001 From: Jack McCracken Date: Mon, 2 Oct 2017 16:35:13 -0400 Subject: Add a better error message when a "null" Origin header occurs --- .../action_controller/metal/request_forgery_protection.rb | 10 ++++++++++ .../test/controller/request_forgery_protection_test.rb | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index d6cd5fd9e0..b2e6f86eeb 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -414,11 +414,21 @@ module ActionController #:nodoc: allow_forgery_protection end + NULL_ORIGIN_MESSAGE = <<-MSG.strip_heredoc + The browser returned a 'null' origin for a request with origin-based forgery protection turned on. This usually + means you have the 'no-referrer' Referrer-Policy header enabled, or that you the request came from a site that + refused to give its origin. This makes it impossible for Rails to verify the source of the requests. Likely the + best solution is to change your referrer policy to something less strict like same-origin or strict-same-origin. + If you cannot change the referrer policy, you can disable origin checking with the + Rails.application.config.action_controller.forgery_protection_origin_check setting. + MSG + # Checks if the request originated from the same origin by looking at the # Origin header. def valid_request_origin? # :doc: if forgery_protection_origin_check # We accept blank origin headers because some user agents don't send it. + raise InvalidAuthenticityToken, NULL_ORIGIN_MESSAGE if request.origin == "null" request.origin.nil? || request.origin == request.base_url else true diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index eb3d2f34a8..4822d85bcb 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -446,6 +446,19 @@ module RequestForgeryProtectionTests end end + def test_should_raise_for_post_with_null_origin + forgery_protection_origin_check do + session[:_csrf_token] = @token + @controller.stub :form_authenticity_token, @token do + exception = assert_raises(ActionController::InvalidAuthenticityToken) do + @request.set_header "HTTP_ORIGIN", "null" + post :index, params: { custom_authenticity_token: @token } + end + assert_match "The browser returned a 'null' origin for a request", exception.message + end + end + end + def test_should_block_post_with_origin_checking_and_wrong_origin old_logger = ActionController::Base.logger logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new -- cgit v1.2.3 From 3812353845fa91bb500691aced10533730c07801 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Sat, 4 Nov 2017 00:38:44 +0400 Subject: Fix Capybara::Webkit::Driver#resize_window deprecation warning >[DEPRECATION] Capybara::Webkit::Driver#resize_window is deprecated. Please use Capybara::Window#resize_to instead. --- actionpack/lib/action_dispatch/system_testing/driver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/system_testing/driver.rb b/actionpack/lib/action_dispatch/system_testing/driver.rb index 770fbde74e..7577d3e68a 100644 --- a/actionpack/lib/action_dispatch/system_testing/driver.rb +++ b/actionpack/lib/action_dispatch/system_testing/driver.rb @@ -59,7 +59,7 @@ module ActionDispatch def register_webkit(app) Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver| - driver.resize_window(*@screen_size) + driver.resize_window_to(*@screen_size) end end -- cgit v1.2.3 From 11f3f0377ba392c15a7fa6130be16db6318a2575 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Fri, 3 Nov 2017 23:33:55 +0000 Subject: Improve docs of ActionDispatch::Routing::Mapper --- actionpack/lib/action_dispatch/routing/mapper.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index dea8387c3d..ded42adee9 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -475,6 +475,16 @@ module ActionDispatch # # resources :users, param: :name # + # The +users+ resource here will have the following routes generated for it: + # + # GET /users(.:format) + # POST /users(.:format) + # GET /users/new(.:format) + # GET /users/:name/edit(.:format) + # GET /users/:name(.:format) + # PATCH/PUT /users/:name(.:format) + # DELETE /users/:name(.:format) + # # You can override ActiveRecord::Base#to_param of a related # model to construct a URL: # @@ -484,8 +494,8 @@ module ActionDispatch # end # end # - # user = User.find_by(name: 'Phusion') - # user_path(user) # => "/users/Phusion" + # user = User.find_by(name: 'Phusion') + # user_path(user) # => "/users/Phusion" # # [:path] # The path prefix for the routes. @@ -1265,7 +1275,7 @@ module ActionDispatch # POST /profile # # === Options - # Takes same options as +resources+. + # Takes same options as resources[rdoc-ref:#resources] def resource(*resources, &block) options = resources.extract_options!.dup @@ -1330,7 +1340,7 @@ module ActionDispatch # DELETE /photos/:photo_id/comments/:id # # === Options - # Takes same options as Base#match as well as: + # Takes same options as match[rdoc-ref:Base#match] as well as: # # [:path_names] # Allows you to change the segment component of the +edit+ and +new+ actions. -- cgit v1.2.3 From e20b049873ed6cdc53d99fe80bb0e292e17f30f5 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 3 Nov 2017 16:35:15 +0900 Subject: Remove unused `calculate_rounded_number` and `digit_count` These methods unused since 5533696. --- .../active_support/number_helper/number_to_rounded_converter.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb index b7ad76bb62..eb528a0583 100644 --- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb @@ -37,14 +37,6 @@ module ActiveSupport private - def calculate_rounded_number(multiplier) - (number / BigDecimal.new(multiplier.to_f.to_s)).round * multiplier - end - - def digit_count(number) - number.zero? ? 1 : (Math.log10(absolute_number(number)) + 1).floor - end - def strip_insignificant_zeros options[:strip_insignificant_zeros] end -- cgit v1.2.3 From 85cda0f6f349744b6f1452d5f1f6cf74ef694393 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 4 Nov 2017 20:11:36 +0900 Subject: s/an/a/ --- railties/test/application/middleware/exceptions_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb index 75afeec905..2d659ade8d 100644 --- a/railties/test/application/middleware/exceptions_test.rb +++ b/railties/test/application/middleware/exceptions_test.rb @@ -102,7 +102,7 @@ module ApplicationTests end end - test "routing to an nonexistent controller when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do + test "routing to a nonexistent controller when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do resources :articles -- cgit v1.2.3 From 4dcb630c6eaf7e4d8e450b3e9f19e38ebbf41d8b Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 5 Nov 2017 13:37:21 +0900 Subject: Generate the correct path in nested scaffold generator Currently, namespaced scaffold generator will generate an incorrect path and the generated file will not work properly. ``` $ ./bin/rails g scaffold admin/user $ ./bin/rails db:migrate $ ./bin/rails t test/controllers # Running: E Error: Admin::UsersControllerTest#test_should_create_admin_user: NameError: undefined local variable or method `admin_admin_users_url' for # Did you mean? admin_users test/controllers/admin/users_controller_test.rb:20:in `block (2 levels) in ' test/controllers/admin/users_controller_test.rb:19:in `block in ' bin/rails test test/controllers/admin/users_controller_test.rb:18 ``` This is because combine `controller_class_path` and `singular_table_name` to generate route. https://github.com/rails/rails/blob/360698aa245b45349d1d1b12e1afb34759515e69/railties/lib/rails/generators/named_base.rb#L172 Normally, if using namspaced generator, table name already contains namespace. Therefore, adding `controller_class_path` adds extra namespace. Since it is special only when explicitly specifying `model-name`, it is modified to change the value only when `model-name`is specified. Follow up of #30729 --- railties/lib/rails/generators/named_base.rb | 18 +++++++++--------- railties/test/generators/named_base_test.rb | 22 ++++++++++++++++++++++ .../test/generators/scaffold_generator_test.rb | 9 ++++++++- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 44f5ab45d3..e0285835a8 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -158,26 +158,26 @@ module Rails def model_resource_name(prefix: "") # :doc: resource_name = "#{prefix}#{singular_table_name}" - if controller_class_path.empty? - resource_name - else + if options[:model_name] "[#{controller_class_path.map { |name| ":" + name }.join(", ")}, #{resource_name}]" + else + resource_name end end def singular_route_name # :doc: - if controller_class_path.empty? - singular_table_name - else + if options[:model_name] "#{controller_class_path.join('_')}_#{singular_table_name}" + else + singular_table_name end end def plural_route_name # :doc: - if controller_class_path.empty? - plural_table_name - else + if options[:model_name] "#{controller_class_path.join('_')}_#{plural_table_name}" + else + plural_table_name end end diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 967754c813..4e61b660d7 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -33,6 +33,17 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "foos", :plural_name assert_name g, "admin.foo", :i18n_scope assert_name g, "admin_foos", :table_name + assert_name g, "admin/foos", :controller_name + assert_name g, %w(admin), :controller_class_path + assert_name g, "Admin::Foos", :controller_class_name + assert_name g, "admin/foos", :controller_file_path + assert_name g, "foos", :controller_file_name + assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_foo", :singular_route_name + assert_name g, "admin_foos", :plural_route_name + assert_name g, "@admin_foo", :redirect_resource_name + assert_name g, "admin_foo", :model_resource_name + assert_name g, "admin_foos", :index_helper end def test_named_generator_attributes_as_ruby @@ -47,6 +58,17 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "foos", :plural_name assert_name g, "admin.foo", :i18n_scope assert_name g, "admin_foos", :table_name + assert_name g, "Admin::Foos", :controller_name + assert_name g, %w(admin), :controller_class_path + assert_name g, "Admin::Foos", :controller_class_name + assert_name g, "admin/foos", :controller_file_path + assert_name g, "foos", :controller_file_name + assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_foo", :singular_route_name + assert_name g, "admin_foos", :plural_route_name + assert_name g, "@admin_foo", :redirect_resource_name + assert_name g, "admin_foo", :model_resource_name + assert_name g, "admin_foos", :index_helper end def test_named_generator_attributes_without_pluralized diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 03322c1c59..b6294c3b94 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -282,7 +282,14 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase /class Admin::RolesTest < ApplicationSystemTestCase/ # Views - %w(index edit new show _form).each do |view| + assert_file "app/views/admin/roles/index.html.erb" do |content| + assert_match("'Show', admin_role", content) + assert_match("'Edit', edit_admin_role_path(admin_role)", content) + assert_match("'Destroy', admin_role", content) + assert_match("'New Admin Role', new_admin_role_path", content) + end + + %w(edit new show _form).each do |view| assert_file "app/views/admin/roles/#{view}.html.erb" end assert_no_file "app/views/layouts/admin/roles.html.erb" -- cgit v1.2.3 From ab293293c33792621c90c02c5516a99e78460663 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 5 Nov 2017 21:23:05 +0900 Subject: Show `RequestForgeryProtection` methods in api doc [ci skip] Several methods of `RequestForgeryProtection` are not showed in the api doc even though `:doc:` is specified. (e.g. `form_authenticity_param`) http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html These methods are listed in the doc of v4.1. http://api.rubyonrails.org/v4.1/classes/ActionController/RequestForgeryProtection.html This is due to the influence of `:nodoc:` added in #18102, methods after `CROSS_ORIGIN_JAVASCRIPT_WARNING` not showed from the doc. Therefore, in order to show the method like originally, added `startdoc` after `CROSS_ORIGIN_JAVASCRIPT_WARNING`. --- actionpack/lib/action_controller/metal/request_forgery_protection.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index d6cd5fd9e0..bd133f24a1 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -248,6 +248,7 @@ module ActionController #:nodoc: "If you know what you're doing, go ahead and disable forgery " \ "protection on this action to permit cross-origin JavaScript embedding." private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING + # :startdoc: # If `verify_authenticity_token` was run (indicating that we have # forgery protection enabled for this request) then also verify that -- cgit v1.2.3 From e3a295664681ea0480a9c95f6bfc776a113ff926 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 1 Nov 2017 10:36:47 +0900 Subject: Ensure `apply_join_dependency` for `collection_cache_key` if eager-loading is needed Fixes #30315. --- activerecord/lib/active_record/collection_cache_key.rb | 3 +++ activerecord/lib/active_record/relation/calculations.rb | 4 ++-- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- activerecord/test/cases/collection_cache_key_test.rb | 10 ++++++++++ activerecord/test/schema/schema.rb | 1 + 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/collection_cache_key.rb b/activerecord/lib/active_record/collection_cache_key.rb index f3e6516414..88b398ad45 100644 --- a/activerecord/lib/active_record/collection_cache_key.rb +++ b/activerecord/lib/active_record/collection_cache_key.rb @@ -12,6 +12,9 @@ module ActiveRecord timestamp = collection.max_by(×tamp_column)._read_attribute(timestamp_column) end else + if collection.eager_loading? + collection = collection.send(:apply_join_dependency) + end column_type = type_for_attribute(timestamp_column.to_s) column = connection.column_name_from_arel_node(collection.arel_attribute(timestamp_column)) select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp" diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 116bddce85..11256ab3d9 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -130,7 +130,7 @@ module ActiveRecord # end def calculate(operation, column_name) if has_include?(column_name) - relation = apply_join_dependency(construct_join_dependency) + relation = apply_join_dependency relation.distinct! if operation.to_s.downcase == "count" relation.calculate(operation, column_name) @@ -180,7 +180,7 @@ module ActiveRecord end if has_include?(column_names.first) - relation = apply_join_dependency(construct_join_dependency) + relation = apply_join_dependency relation.pluck(*column_names) else relation = spawn diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 707245bab2..18566b5662 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -394,7 +394,7 @@ module ActiveRecord ) end - def apply_join_dependency(join_dependency) + def apply_join_dependency(join_dependency = construct_join_dependency) relation = except(:includes, :eager_load, :preload).joins!(join_dependency) if using_limitable_reflections?(join_dependency.reflections) diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb index c137693211..19d6464a22 100644 --- a/activerecord/test/cases/collection_cache_key_test.rb +++ b/activerecord/test/cases/collection_cache_key_test.rb @@ -73,6 +73,16 @@ module ActiveRecord assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3 end + test "cache_key for relation with includes" do + comments = Comment.includes(:post).where("posts.type": "Post") + assert_match(/\Acomments\/query-(\h+)-(\d+)-(\d+)\z/, comments.cache_key) + end + + test "cache_key for loaded relation with includes" do + comments = Comment.includes(:post).where("posts.type": "Post").load + assert_match(/\Acomments\/query-(\h+)-(\d+)-(\d+)\z/, comments.cache_key) + end + test "it triggers at most one query" do developers = Developer.where(name: "David") diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 8f872c38ba..a4505a4892 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -191,6 +191,7 @@ ActiveRecord::Schema.define do t.string :resource_id t.string :resource_type t.integer :developer_id + t.datetime :updated_at t.datetime :deleted_at t.integer :comments end -- cgit v1.2.3 From 8a2ee3d8c6921ce34e597658e3f2b43b41423d1d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 16 Oct 2017 01:33:40 +0900 Subject: Ensure `apply_join_dependency` for `update_all` and `delete_all` if eager-loading is needed If a relation has eager-loading values, `count` and `exists?` works properly, but `update_all` and `delete_all` doesn't work due to missing `apply_join_dependency`. It should be applied to work consistently. Fixes #28863. --- activerecord/lib/active_record/relation.rb | 10 ++++++ activerecord/test/cases/persistence_test.rb | 49 ++++++++++++++++++----------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 997cfe4b5e..7615fb6ee9 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -359,6 +359,11 @@ module ActiveRecord def update_all(updates) raise ArgumentError, "Empty list of attributes to change" if updates.blank? + if eager_loading? + relation = apply_join_dependency + return relation.update_all(updates) + end + stmt = Arel::UpdateManager.new stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates)) @@ -423,6 +428,11 @@ module ActiveRecord raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}") end + if eager_loading? + relation = apply_join_dependency + return relation.delete_all + end + stmt = Arel::DeleteManager.new stmt.from(table) diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 6cbe18cc8c..f088c064f5 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -94,27 +94,31 @@ class PersistenceTest < ActiveRecord::TestCase end def test_delete_all_with_joins_and_where_part_is_hash - where_args = { toys: { name: "Bone" } } - count = Pet.joins(:toys).where(where_args).count + pets = Pet.joins(:toys).where(toys: { name: "Bone" }) - assert_equal count, 1 - assert_equal count, Pet.joins(:toys).where(where_args).delete_all + assert_equal true, pets.exists? + assert_equal pets.count, pets.delete_all + end + + def test_delete_all_with_joins_and_where_part_is_not_hash + pets = Pet.joins(:toys).where("toys.name = ?", "Bone") + + assert_equal true, pets.exists? + assert_equal pets.count, pets.delete_all end def test_delete_all_with_left_joins - where_args = { toys: { name: "Bone" } } - count = Pet.left_joins(:toys).where(where_args).count + pets = Pet.left_joins(:toys).where(toys: { name: "Bone" }) - assert_equal count, 1 - assert_equal count, Pet.left_joins(:toys).where(where_args).delete_all + assert_equal true, pets.exists? + assert_equal pets.count, pets.delete_all end - def test_delete_all_with_joins_and_where_part_is_not_hash - where_args = ["toys.name = ?", "Bone"] - count = Pet.joins(:toys).where(where_args).count + def test_delete_all_with_includes + pets = Pet.includes(:toys).where(toys: { name: "Bone" }) - assert_equal count, 1 - assert_equal count, Pet.joins(:toys).where(where_args).delete_all + assert_equal true, pets.exists? + assert_equal pets.count, pets.delete_all end def test_increment_attribute @@ -496,17 +500,24 @@ class PersistenceTest < ActiveRecord::TestCase end def test_update_all_with_joins - where_args = { toys: { name: "Bone" } } - count = Pet.left_joins(:toys).where(where_args).count + pets = Pet.joins(:toys).where(toys: { name: "Bone" }) - assert_equal count, Pet.joins(:toys).where(where_args).update_all(name: "Bob") + assert_equal true, pets.exists? + assert_equal pets.count, pets.update_all(name: "Bob") end def test_update_all_with_left_joins - where_args = { toys: { name: "Bone" } } - count = Pet.left_joins(:toys).where(where_args).count + pets = Pet.left_joins(:toys).where(toys: { name: "Bone" }) + + assert_equal true, pets.exists? + assert_equal pets.count, pets.update_all(name: "Bob") + end + + def test_update_all_with_includes + pets = Pet.includes(:toys).where(toys: { name: "Bone" }) - assert_equal count, Pet.left_joins(:toys).where(where_args).update_all(name: "Bob") + assert_equal true, pets.exists? + assert_equal pets.count, pets.update_all(name: "Bob") end def test_update_all_with_non_standard_table_name -- cgit v1.2.3 From 7308991630af3fb8a2a7e2cde9fd322316153ec2 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sun, 5 Nov 2017 19:19:41 +0000 Subject: Execute `ConfirmationValidator` validation when `_confirmation`'s value is `false` --- activemodel/CHANGELOG.md | 4 ++++ activemodel/lib/active_model/validations/confirmation.rb | 2 +- .../test/cases/validations/confirmation_validation_test.rb | 13 +++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 82e3a7f4dd..794744c646 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,3 +1,7 @@ +* Execute `ConfirmationValidator` validation when `_confirmation`'s value is `false`. + + *bogdanvlviv* + * Allow passing a Proc or Symbol to length validator options. *Matt Rohrer* diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb index 0abec56b68..1b5d5b09ab 100644 --- a/activemodel/lib/active_model/validations/confirmation.rb +++ b/activemodel/lib/active_model/validations/confirmation.rb @@ -9,7 +9,7 @@ module ActiveModel end def validate_each(record, attribute, value) - if (confirmed = record.send("#{attribute}_confirmation")) + unless (confirmed = record.send("#{attribute}_confirmation")).nil? unless confirmation_value_equal?(record, attribute, value, confirmed) human_attribute_name = record.class.human_attribute_name(attribute) record.errors.add(:"#{attribute}_confirmation", :confirmation, options.except(:case_sensitive).merge!(attribute: human_attribute_name)) diff --git a/activemodel/test/cases/validations/confirmation_validation_test.rb b/activemodel/test/cases/validations/confirmation_validation_test.rb index e84415a868..8b2c65289b 100644 --- a/activemodel/test/cases/validations/confirmation_validation_test.rb +++ b/activemodel/test/cases/validations/confirmation_validation_test.rb @@ -37,6 +37,19 @@ class ConfirmationValidationTest < ActiveModel::TestCase assert t.valid? end + def test_validates_confirmation_of_with_boolean_attribute + Topic.validates_confirmation_of(:approved) + + t = Topic.new(approved: true, approved_confirmation: nil) + assert t.valid? + + t.approved_confirmation = false + assert t.invalid? + + t.approved_confirmation = true + assert t.valid? + end + def test_validates_confirmation_of_for_ruby_class Person.validates_confirmation_of :karma -- cgit v1.2.3 From e617fb57f5a388d5f0a47fd5e576588dd10066b0 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 30 Oct 2017 23:55:48 +0900 Subject: Fix preloading polymorphic association when through association has already loaded If through association has already loaded, `source_type` is ignored to loaded through records. The loaded records should be filtered by `source_type` in that case. Fixes #30904. --- .../associations/preloader/through_association.rb | 20 ++++++++++++++++---- .../associations/nested_through_associations_test.rb | 11 +++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index fa32cc5553..5bd49b041a 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -13,18 +13,30 @@ module ActiveRecord end def associated_records_by_owner(preloader) + already_loaded = owners.first.association(through_reflection.name).loaded? through_scope = through_scope() - preloader.preload(owners, - through_reflection.name, - through_scope) + unless already_loaded + preloader.preload(owners, through_reflection.name, through_scope) + end through_records = owners.map do |owner| center = owner.association(through_reflection.name).target [owner, Array(center)] end - reset_association(owners, through_reflection.name, through_scope) + if already_loaded + if source_type = reflection.options[:source_type] + through_records.map! do |owner, center| + center = center.select do |record| + record[reflection.foreign_type] == source_type + end + [owner, center] + end + end + else + reset_association(owners, through_reflection.name, through_scope) + end middle_records = through_records.flat_map(&:last) diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 4cf5a9ffc4..11ee45917f 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -579,6 +579,17 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase assert !c.post_taggings.empty? end + def test_polymorphic_has_many_through_when_through_association_has_already_loaded + cake_designer = CakeDesigner.create!(chef: Chef.new) + drink_designer = DrinkDesigner.create!(chef: Chef.new) + department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef]) + Hotel.create!(departments: [department]) + hotel = Hotel.includes(:chefs, :cake_designers, :drink_designers).take + + assert_equal [cake_designer], hotel.cake_designers + assert_equal [drink_designer], hotel.drink_designers + end + def test_polymorphic_has_many_through_joined_different_table_twice cake_designer = CakeDesigner.create!(chef: Chef.new) drink_designer = DrinkDesigner.create!(chef: Chef.new) -- cgit v1.2.3 From e0bef22665f93e88f6b2f3ac6bd55543ed0d6343 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 6 Nov 2017 06:40:36 +0900 Subject: Fix preloading polymorphic multi-level through association This is partially fixed by e617fb57 when through association has already loaded. Otherwise, second level through association should respect `preload_scope`. Fixes #30242. Closes #30076. [Ryuta Kamizono & CicholGricenchos] --- .../associations/preloader/through_association.rb | 8 +++++++- .../cases/associations/nested_through_associations_test.rb | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index 5bd49b041a..b16fca7dc9 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -40,7 +40,11 @@ module ActiveRecord middle_records = through_records.flat_map(&:last) - reflection_scope = reflection_scope() if reflection.scope + if preload_scope + reflection_scope = reflection_scope().merge(preload_scope) + elsif reflection.scope + reflection_scope = reflection_scope() + end preloaders = preloader.preload(middle_records, source_reflection.name, @@ -70,6 +74,8 @@ module ActiveRecord rhs_records end end + end.tap do + reset_association(middle_records, source_reflection.name, preload_scope) end end diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 11ee45917f..65d30d011b 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -579,6 +579,17 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase assert !c.post_taggings.empty? end + def test_polymorphic_has_many_through_when_through_association_has_not_loaded + cake_designer = CakeDesigner.create!(chef: Chef.new) + drink_designer = DrinkDesigner.create!(chef: Chef.new) + department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef]) + Hotel.create!(departments: [department]) + hotel = Hotel.includes(:cake_designers, :drink_designers).take + + assert_equal [cake_designer], hotel.cake_designers + assert_equal [drink_designer], hotel.drink_designers + end + def test_polymorphic_has_many_through_when_through_association_has_already_loaded cake_designer = CakeDesigner.create!(chef: Chef.new) drink_designer = DrinkDesigner.create!(chef: Chef.new) -- cgit v1.2.3 From ffc1ed9bd54f3cf746f7ddf798e000fc025edef6 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 6 Nov 2017 08:29:40 +0900 Subject: `scoping` should respect current class and STI constraint (#29199) A relation includes `klass`, so it can not be used as it is if current class is different from `current_scope.klass`. It should be created new relation by current class to respect the klass and STI constraint. Fixes #17603. Fixes #23576. --- activerecord/lib/active_record/scoping/named.rb | 8 +++++++- activerecord/test/cases/scoping/relation_scoping_test.rb | 14 ++++++++++++++ activerecord/test/models/comment.rb | 4 ++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 6fa096c1fe..310af72c41 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -24,8 +24,14 @@ module ActiveRecord # You can define a scope that applies to all finders using # {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope]. def all + current_scope = self.current_scope + if current_scope - current_scope.clone + if self == current_scope.klass + current_scope.clone + else + relation.merge!(current_scope) + end else default_scoped end diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index f3b84d88c2..116f8e83aa 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -240,6 +240,20 @@ class RelationScopingTest < ActiveRecord::TestCase assert_nil SpecialComment.current_scope end + def test_scoping_respects_current_class + Comment.unscoped do + assert_equal "a comment...", Comment.all.what_are_you + assert_equal "a special comment...", SpecialComment.all.what_are_you + end + end + + def test_scoping_respects_sti_constraint + Comment.unscoped do + assert_equal comments(:greetings), Comment.find(1) + assert_raises(ActiveRecord::RecordNotFound) { SpecialComment.find(1) } + end + end + def test_circular_joins_with_scoping_does_not_crash posts = Post.joins(comments: :post).scoping do Post.first(10) diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index 740aa593ac..5ab433f2d9 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -60,6 +60,10 @@ end class SpecialComment < Comment default_scope { where(deleted_at: nil) } + + def self.what_are_you + "a special comment..." + end end class SubSpecialComment < SpecialComment -- cgit v1.2.3 From 8847e608b9a0f7a3f8d65832e1815b4ab1f021f3 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 6 Nov 2017 13:11:04 +0900 Subject: Explicitly pass window handle to `resize_window_to` Unlike `resize_window`, `resize_window_to` has three arguments. https://github.com/thoughtbot/capybara-webkit/blob/d63c3c8e3ae844f0c59359532a6dcb50f4a64d0a/lib/capybara/webkit/driver.rb#L135-L143 Therefore, if pass only width and height just like `resize_window`, `ArgumentError`will be raised. To prevent this, explicitly pass window handler. Follow up of #31046 --- actionpack/lib/action_dispatch/system_testing/driver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/system_testing/driver.rb b/actionpack/lib/action_dispatch/system_testing/driver.rb index 7577d3e68a..2687772b4b 100644 --- a/actionpack/lib/action_dispatch/system_testing/driver.rb +++ b/actionpack/lib/action_dispatch/system_testing/driver.rb @@ -59,7 +59,7 @@ module ActionDispatch def register_webkit(app) Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver| - driver.resize_window_to(*@screen_size) + driver.resize_window_to(driver.current_window_handle, *@screen_size) end end -- cgit v1.2.3 From bb6d369f891963f8ed6ec3eeaaba3066c438aaee Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 6 Nov 2017 16:47:46 +0900 Subject: Remove unused require Since f182831, this file does not use methods added by `module/introspection`. --- railties/lib/rails/generators/named_base.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index e0285835a8..60625283ac 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "active_support/core_ext/module/introspection" require "rails/generators/base" require "rails/generators/generated_attribute" -- cgit v1.2.3 From afc188fd7600b62bb9b43cd41eb9b9ea4d77a2e2 Mon Sep 17 00:00:00 2001 From: Skander Date: Mon, 6 Nov 2017 04:54:25 -0500 Subject: Fix french spelling mistake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trés -> Très https://fr.wiktionary.org/wiki/tr%C3%A8s --- activesupport/lib/active_support/inflector/transliterate.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb index e689f0718e..9fb3a2e0af 100644 --- a/activesupport/lib/active_support/inflector/transliterate.rb +++ b/activesupport/lib/active_support/inflector/transliterate.rb @@ -71,23 +71,23 @@ module ActiveSupport # a 'pretty' URL. # # parameterize("Donald E. Knuth") # => "donald-e-knuth" - # parameterize("^trés|Jolie-- ") # => "tres-jolie" + # parameterize("^très|Jolie-- ") # => "tres-jolie" # # To use a custom separator, override the `separator` argument. # # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth" - # parameterize("^trés|Jolie__ ", separator: '_') # => "tres_jolie" + # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie" # # To preserve the case of the characters in a string, use the `preserve_case` argument. # # parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth" - # parameterize("^trés|Jolie-- ", preserve_case: true) # => "tres-Jolie" + # parameterize("^très|Jolie-- ", preserve_case: true) # => "tres-Jolie" # # It preserves dashes and underscores unless they are used as separators: # - # parameterize("^trés|Jolie__ ") # => "tres-jolie__" - # parameterize("^trés|Jolie-- ", separator: "_") # => "tres_jolie--" - # parameterize("^trés_Jolie-- ", separator: ".") # => "tres_jolie--" + # parameterize("^très|Jolie__ ") # => "tres-jolie__" + # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--" + # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--" # def parameterize(string, separator: "-", preserve_case: false) # Replace accented chars with their ASCII equivalents. -- cgit v1.2.3 From 0ddde0a8fca6a0ca3158e3329713959acd65605d Mon Sep 17 00:00:00 2001 From: Andrew White Date: Mon, 6 Nov 2017 14:46:42 +0000 Subject: Fix acronym support in `humanize` Acronym inflections are stored with lowercase keys in the hash but the match wasn't being lowercased before being looked up in the hash. This shouldn't have any performance impact because before it would fail to find the acronym and perform the `downcase` operation anyway. Fixes #31052. --- activesupport/CHANGELOG.md | 11 +++++++++++ activesupport/lib/active_support/inflector/methods.rb | 2 +- activesupport/test/inflector_test.rb | 13 +++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index d664bc2027..9c27c68919 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,14 @@ +* Fix acronym support in `humanize` + + Acronym inflections are stored with lowercase keys in the hash but + the match wasn't being lowercased before being looked up in the hash. + This shouldn't have any performance impact because before it would + fail to find the acronym and perform the `downcase` operation anyway. + + Fixes #31052. + + *Andrew White* + * Add same method signature for `Time#prev_year` and `Time#next_year` in accordance with `Date#prev_year`, `Date#next_year`. diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 72de42b1c3..3357b5eb32 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -138,7 +138,7 @@ module ActiveSupport result.tr!("_".freeze, " ".freeze) result.gsub!(/([a-z\d]*)/i) do |match| - "#{inflections.acronyms[match] || match.downcase}" + "#{inflections.acronyms[match.downcase] || match.downcase}" end if capitalize diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index eeec0ab1a5..6cc1039d8e 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -356,6 +356,19 @@ class InflectorTest < ActiveSupport::TestCase assert_equal("Col rpted bugs", ActiveSupport::Inflector.humanize("COL_rpted_bugs")) end + def test_humanize_with_acronyms + ActiveSupport::Inflector.inflections do |inflect| + inflect.acronym "LAX" + inflect.acronym "SFO" + end + assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("LAX ROUNDTRIP TO SFO")) + assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("LAX ROUNDTRIP TO SFO", capitalize: false)) + assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("lax roundtrip to sfo")) + assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("lax roundtrip to sfo", capitalize: false)) + assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("Lax Roundtrip To Sfo")) + assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("Lax Roundtrip To Sfo", capitalize: false)) + end + def test_constantize run_constantize_tests_on do |string| ActiveSupport::Inflector.constantize(string) -- cgit v1.2.3 From edcc0a714d911468caf7db0879fc7a3d1185ee3c Mon Sep 17 00:00:00 2001 From: Bogdan Gusiev Date: Mon, 6 Nov 2017 17:32:54 +0200 Subject: Refactor Preloader Code --- .../associations/preloader/association.rb | 4 +- .../associations/preloader/has_many_through.rb | 10 -- .../associations/preloader/through_association.rb | 107 ++++++++------------- 3 files changed, 40 insertions(+), 81 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 607d376a08..b0c41c7d5f 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -17,7 +17,7 @@ module ActiveRecord end def run(preloader) - associated_records_by_owner(preloader).each do |owner, records| + associated_records_by_owner(preloader) do |owner, records| associate_records_to_owner(owner, records) end end @@ -41,7 +41,7 @@ module ActiveRecord end owners.each_with_object({}) do |owner, result| - result[owner] = records[convert_key(owner[owner_key_name])] || [] + yield(owner, records[convert_key(owner[owner_key_name])] || []) end end diff --git a/activerecord/lib/active_record/associations/preloader/has_many_through.rb b/activerecord/lib/active_record/associations/preloader/has_many_through.rb index 0639fdca44..3e17d07a33 100644 --- a/activerecord/lib/active_record/associations/preloader/has_many_through.rb +++ b/activerecord/lib/active_record/associations/preloader/has_many_through.rb @@ -5,16 +5,6 @@ module ActiveRecord class Preloader class HasManyThrough < CollectionAssociation #:nodoc: include ThroughAssociation - - def associated_records_by_owner(preloader) - records_by_owner = super - - if reflection_scope.distinct_value - records_by_owner.each_value(&:uniq!) - end - - records_by_owner - end end end end diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index b16fca7dc9..1bb7e98231 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -13,86 +13,45 @@ module ActiveRecord end def associated_records_by_owner(preloader) - already_loaded = owners.first.association(through_reflection.name).loaded? - through_scope = through_scope() - - unless already_loaded - preloader.preload(owners, through_reflection.name, through_scope) - end - - through_records = owners.map do |owner| - center = owner.association(through_reflection.name).target - [owner, Array(center)] - end + already_loaded = owners.first.association(through_reflection.name).loaded? + through_scope = through_scope() + reflection_scope = target_reflection_scope + through_preloaders = preloader.preload(owners, through_reflection.name, through_scope) + middle_records = through_preloaders.flat_map(&:preloaded_records) + preloaders = preloader.preload(middle_records, source_reflection.name, reflection_scope) + @preloaded_records = preloaders.flat_map(&:preloaded_records) - if already_loaded - if source_type = reflection.options[:source_type] - through_records.map! do |owner, center| - center = center.select do |record| + owners.each do |owner| + through_records = Array(owner.association(through_reflection.name).target) + if already_loaded + if source_type = reflection.options[:source_type] + through_records = through_records.select do |record| record[reflection.foreign_type] == source_type end - [owner, center] end + else + owner.association(through_reflection.name).reset if through_scope end - else - reset_association(owners, through_reflection.name, through_scope) - end - - middle_records = through_records.flat_map(&:last) - - if preload_scope - reflection_scope = reflection_scope().merge(preload_scope) - elsif reflection.scope - reflection_scope = reflection_scope() - end - - preloaders = preloader.preload(middle_records, - source_reflection.name, - reflection_scope) - - @preloaded_records = preloaders.flat_map(&:preloaded_records) - - middle_to_pl = preloaders.each_with_object({}) do |pl, h| - pl.owners.each { |middle| - h[middle] = pl - } - end - - through_records.each_with_object({}) do |(lhs, center), records_by_owner| - pl_to_middle = center.group_by { |record| middle_to_pl[record] } - - records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles| - rhs_records = middles.flat_map { |r| - r.association(source_reflection.name).target - }.compact - - # Respect the order on `reflection_scope` if it exists, else use the natural order. - if reflection_scope && !reflection_scope.order_values.empty? - @id_map ||= id_to_index_map @preloaded_records - rhs_records.sort_by { |rhs| @id_map[rhs] } - else - rhs_records - end + result = through_records.flat_map do |record| + association = record.association(source_reflection.name) + target = association.target + association.reset if preload_scope + target end - end.tap do - reset_association(middle_records, source_reflection.name, preload_scope) + result.compact! + if reflection_scope + result.sort_by! { |rhs| preload_index[rhs] } if reflection_scope.order_values.any? + result.uniq! if reflection_scope.distinct_value + end + yield(owner, result) end end private - def id_to_index_map(ids) - id_map = {} - ids.each_with_index { |id, index| id_map[id] = index } - id_map - end - - def reset_association(owners, association_name, should_reset) - # Don't cache the association - we would only be caching a subset - if should_reset - owners.each { |owner| - owner.association(association_name).reset - } + def preload_index + @preload_index ||= @preloaded_records.each_with_object({}).with_index do |(id, result), index| + result[id] = index end end @@ -133,6 +92,16 @@ module ActiveRecord scope unless scope.empty_scope? end + + def target_reflection_scope + if preload_scope + reflection_scope.merge(preload_scope) + elsif reflection.scope + reflection_scope + else + nil + end + end end end end -- cgit v1.2.3 From 1fe7168df0a23b69539b60327943495a3dded9e5 Mon Sep 17 00:00:00 2001 From: Dan Ott Date: Mon, 6 Nov 2017 10:56:24 -0500 Subject: Resolve Minitest 6 deprecation in assert_no_changes These changes resolve a deprecation warning in `assert_no_changes` when asserting that an expression evaluates to `nil` before and after the passed block is evaluated. The smallest demonstration of this edge case: ```ruby assert_no_changes "nil" do true # noop end ``` Under the covers, this is evaluating ```ruby assert_equal nil, nil ``` Minitest 5 issues a deprecation warning, and Minitest will fail completely. For additional context, the motivations and implications of this change to Minitest have been discussed at length in [seattlerb/minitest#666][]. [seattlerb/minitest#666]: https://github.com/seattlerb/minitest/issues/666 --- activesupport/lib/active_support/testing/assertions.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index e2bc51ff7a..46eed73d24 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -190,7 +190,12 @@ module ActiveSupport error = "#{expression.inspect} did change to #{after}" error = "#{message}.\n#{error}" if message - assert_equal before, after, error + + if before.nil? + assert_nil after, error + else + assert_equal before, after, error + end retval end -- cgit v1.2.3 From 01c703248396528d9f3398d0f9d0143831e9d0dc Mon Sep 17 00:00:00 2001 From: Keenan Brock Date: Thu, 9 Mar 2017 11:48:44 -0500 Subject: Properly check transaction in persistence ``` [NoMethodError]: undefined method `state' for nil:NilClass Method:[rescue in block in refresh] ``` In `within_new_transaction`, there is the possibility that `begin_transaction` returns a `nil`. (i.e.: so `transaction = nil`) So this method is checking `transaction` for nil in 2 spots. Unfortunately, there is one line that is not checking `transaction` for `nil` That line, `commit_transaction`, throws an exception for us in AR 5.0.0.1 The problem with the method is finally realized in the error checking itself. it calls `transaction.state` (i.e.: nil.state) and that is the final exception raised. The actual underlying (user) issue is hidden by this line. Solution is test transaction for nil. --- .../lib/active_record/connection_adapters/abstract/transaction.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 147e16e9fa..d9ac8db6a8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -240,7 +240,7 @@ module ActiveRecord rollback_transaction if transaction else begin - commit_transaction + commit_transaction if transaction rescue Exception rollback_transaction(transaction) unless transaction.state.completed? raise -- cgit v1.2.3 From 4a835aa3236eedb135ccf8b59ed3c03e040b8b01 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sun, 6 Aug 2017 20:41:31 +0000 Subject: Add --skip-active-storage and do so automatically when --skip-active-record is used Closes #30102 Revert part 787fe90dc0a7c5b91bb5af51f2858ea8c4676268 --skip-active-storage pass throughs `rails plugin new` Add changelog entry about default initialization of Active Storage --- railties/CHANGELOG.md | 6 + railties/lib/rails/all.rb | 2 +- railties/lib/rails/app_updater.rb | 11 +- railties/lib/rails/generators/app_base.rb | 123 +++++++++++++-------- .../rails/generators/rails/app/app_generator.rb | 8 +- .../rails/generators/rails/app/templates/Gemfile | 3 +- .../app/assets/javascripts/application.js.tt | 2 + .../rails/app/templates/config/application.rb | 2 +- .../config/environments/development.rb.tt | 2 + .../templates/config/environments/production.rb.tt | 2 + .../app/templates/config/environments/test.rb.tt | 3 + .../rails/generators/rails/app/templates/gitignore | 2 + .../generators/rails/plugin/plugin_generator.rb | 2 +- .../generators/rails/plugin/templates/gitignore | 3 + .../rails/plugin/templates/rails/application.rb | 2 +- .../rails/plugin/templates/rails/javascripts.js | 3 + railties/test/generators/app_generator_test.rb | 61 ++++++++++ railties/test/generators/plugin_generator_test.rb | 5 +- .../test/generators/plugin_test_runner_test.rb | 2 +- railties/test/generators/shared_generator_tests.rb | 91 +++++++++++++++ 20 files changed, 270 insertions(+), 65 deletions(-) diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 20603dedaf..e69a1231b1 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,9 @@ +* `rails new` and `rails plugin new` get `Active Storage` by default. + Add ability to skip `Active Storage` with `--skip-active-storage` + and do so automatically when `--skip-active-record` is used. + + *bogdanvlviv* + * Gemfile for new apps: upgrade redis-rb from ~> 3.0 to 4.0. *Jeremy Daer* diff --git a/railties/lib/rails/all.rb b/railties/lib/rails/all.rb index e55b2e2433..f5dccd2381 100644 --- a/railties/lib/rails/all.rb +++ b/railties/lib/rails/all.rb @@ -4,12 +4,12 @@ require "rails" %w( active_record/railtie + active_storage/engine action_controller/railtie action_view/railtie action_mailer/railtie active_job/railtie action_cable/engine - active_storage/engine rails/test_unit/railtie sprockets/railtie ).each do |railtie| diff --git a/railties/lib/rails/app_updater.rb b/railties/lib/rails/app_updater.rb index c2436a69f9..a076d082d5 100644 --- a/railties/lib/rails/app_updater.rb +++ b/railties/lib/rails/app_updater.rb @@ -21,11 +21,12 @@ module Rails private def generator_options options = { api: !!Rails.application.config.api_only, update: true } - options[:skip_active_record] = !defined?(ActiveRecord::Railtie) - options[:skip_action_mailer] = !defined?(ActionMailer::Railtie) - options[:skip_action_cable] = !defined?(ActionCable::Engine) - options[:skip_sprockets] = !defined?(Sprockets::Railtie) - options[:skip_puma] = !defined?(Puma) + options[:skip_active_record] = !defined?(ActiveRecord::Railtie) + options[:skip_active_storage] = !defined?(ActiveStorage::Engine) || !defined?(ActiveRecord::Railtie) + options[:skip_action_mailer] = !defined?(ActionMailer::Railtie) + options[:skip_action_cable] = !defined?(ActionCable::Engine) + options[:skip_sprockets] = !defined?(Sprockets::Railtie) + options[:skip_puma] = !defined?(Puma) options end end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index bc00adabae..2809c433f6 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -26,75 +26,78 @@ module Rails end def self.add_shared_options_for(name) - class_option :template, type: :string, aliases: "-m", - desc: "Path to some #{name} template (can be a filesystem path or URL)" + class_option :template, type: :string, aliases: "-m", + desc: "Path to some #{name} template (can be a filesystem path or URL)" - class_option :database, type: :string, aliases: "-d", default: "sqlite3", - desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})" + class_option :database, type: :string, aliases: "-d", default: "sqlite3", + desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})" - class_option :skip_yarn, type: :boolean, default: false, - desc: "Don't use Yarn for managing JavaScript dependencies" + class_option :skip_yarn, type: :boolean, default: false, + desc: "Don't use Yarn for managing JavaScript dependencies" - class_option :skip_gemfile, type: :boolean, default: false, - desc: "Don't create a Gemfile" + class_option :skip_gemfile, type: :boolean, default: false, + desc: "Don't create a Gemfile" - class_option :skip_git, type: :boolean, aliases: "-G", default: false, - desc: "Skip .gitignore file" + class_option :skip_git, type: :boolean, aliases: "-G", default: false, + desc: "Skip .gitignore file" - class_option :skip_keeps, type: :boolean, default: false, - desc: "Skip source control .keep files" + class_option :skip_keeps, type: :boolean, default: false, + desc: "Skip source control .keep files" - class_option :skip_action_mailer, type: :boolean, aliases: "-M", - default: false, - desc: "Skip Action Mailer files" + class_option :skip_action_mailer, type: :boolean, aliases: "-M", + default: false, + desc: "Skip Action Mailer files" - class_option :skip_active_record, type: :boolean, aliases: "-O", default: false, - desc: "Skip Active Record files" + class_option :skip_active_record, type: :boolean, aliases: "-O", default: false, + desc: "Skip Active Record files" - class_option :skip_puma, type: :boolean, aliases: "-P", default: false, - desc: "Skip Puma related files" + class_option :skip_active_storage, type: :boolean, default: false, + desc: "Skip Active Storage files" - class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false, - desc: "Skip Action Cable files" + class_option :skip_puma, type: :boolean, aliases: "-P", default: false, + desc: "Skip Puma related files" - class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false, - desc: "Skip Sprockets files" + class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false, + desc: "Skip Action Cable files" - class_option :skip_spring, type: :boolean, default: false, - desc: "Don't install Spring application preloader" + class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false, + desc: "Skip Sprockets files" - class_option :skip_listen, type: :boolean, default: false, - desc: "Don't generate configuration that depends on the listen gem" + class_option :skip_spring, type: :boolean, default: false, + desc: "Don't install Spring application preloader" - class_option :skip_coffee, type: :boolean, default: false, - desc: "Don't use CoffeeScript" + class_option :skip_listen, type: :boolean, default: false, + desc: "Don't generate configuration that depends on the listen gem" - class_option :skip_javascript, type: :boolean, aliases: "-J", default: false, - desc: "Skip JavaScript files" + class_option :skip_coffee, type: :boolean, default: false, + desc: "Don't use CoffeeScript" - class_option :skip_turbolinks, type: :boolean, default: false, - desc: "Skip turbolinks gem" + class_option :skip_javascript, type: :boolean, aliases: "-J", default: false, + desc: "Skip JavaScript files" - class_option :skip_test, type: :boolean, aliases: "-T", default: false, - desc: "Skip test files" + class_option :skip_turbolinks, type: :boolean, default: false, + desc: "Skip turbolinks gem" - class_option :skip_system_test, type: :boolean, default: false, - desc: "Skip system test files" + class_option :skip_test, type: :boolean, aliases: "-T", default: false, + desc: "Skip test files" - class_option :dev, type: :boolean, default: false, - desc: "Setup the #{name} with Gemfile pointing to your Rails checkout" + class_option :skip_system_test, type: :boolean, default: false, + desc: "Skip system test files" - class_option :edge, type: :boolean, default: false, - desc: "Setup the #{name} with Gemfile pointing to Rails repository" + class_option :dev, type: :boolean, default: false, + desc: "Setup the #{name} with Gemfile pointing to your Rails checkout" - class_option :rc, type: :string, default: nil, - desc: "Path to file containing extra configuration options for rails command" + class_option :edge, type: :boolean, default: false, + desc: "Setup the #{name} with Gemfile pointing to Rails repository" - class_option :no_rc, type: :boolean, default: false, - desc: "Skip loading of extra configuration options from .railsrc file" + class_option :rc, type: :string, default: nil, + desc: "Path to file containing extra configuration options for rails command" - class_option :help, type: :boolean, aliases: "-h", group: :rails, - desc: "Show this help message and quit" + class_option :no_rc, type: :boolean, default: false, + desc: "Skip loading of extra configuration options from .railsrc file" + + class_option :help, type: :boolean, aliases: "-h", group: :rails, + desc: "Show this help message and quit" end def initialize(*args) @@ -193,11 +196,29 @@ module Rails end def include_all_railties? # :doc: - options.values_at(:skip_active_record, :skip_action_mailer, :skip_test, :skip_sprockets, :skip_action_cable).none? + [ + options.values_at( + :skip_active_record, + :skip_action_mailer, + :skip_test, + :skip_sprockets, + :skip_action_cable + ), + skip_active_storage? + ].flatten.none? end def comment_if(value) # :doc: - options[value] ? "# " : "" + question = "#{value}?" + + comment = + if respond_to?(question, true) + send(question) + else + options[value] + end + + comment ? "# " : "" end def keeps? # :doc: @@ -208,6 +229,10 @@ module Rails !options[:skip_active_record] && options[:database] == "sqlite3" end + def skip_active_storage? # :doc: + options[:skip_active_storage] || options[:skip_active_record] + end + class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out) def initialize(name, version, comment, options = {}, commented_out = false) super diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 46954f64b5..15c5b973ca 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -114,7 +114,7 @@ module Rails template "cable.yml" unless options[:skip_action_cable] template "puma.rb" unless options[:skip_puma] template "spring.rb" if spring_install? - template "storage.yml" + template "storage.yml" unless skip_active_storage? directory "environments" directory "initializers" @@ -139,7 +139,7 @@ module Rails template "config/cable.yml" end - if !active_storage_config_exist + if !skip_active_storage? && !active_storage_config_exist template "config/storage.yml" end @@ -355,6 +355,10 @@ module Rails build(:system_test) if depends_on_system_test? end + def create_storage_files + build(:storage) unless skip_active_storage? + end + def create_tmp_files build(:tmp) end diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index 95e8be96b6..ee9db83ca2 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -20,9 +20,10 @@ ruby <%= "'#{RUBY_VERSION}'" -%> # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' - +<% unless skip_active_storage? -%> # Use ActiveStorage variant # gem 'mini_magick', '~> 4.8' +<% end -%> # Use Capistrano for deployment # gem 'capistrano-rails', group: :development diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt index 62fd04f113..5183bcd256 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt @@ -12,7 +12,9 @@ // <% unless options[:skip_javascript] -%> //= require rails-ujs +<% unless skip_active_storage? -%> //= require activestorage +<% end -%> <% unless options[:skip_turbolinks] -%> //= require turbolinks <% end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index dde09edb94..9e03e86771 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -8,10 +8,10 @@ require "rails" require "active_model/railtie" require "active_job/railtie" <%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" require "action_controller/railtie" <%= comment_if :skip_action_mailer %>require "action_mailer/railtie" require "action_view/railtie" -require "active_storage/engine" <%= comment_if :skip_action_cable %>require "action_cable/engine" <%= comment_if :skip_sprockets %>require "sprockets/railtie" <%= comment_if :skip_test %>require "rails/test_unit/railtie" diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index 98689cc30d..2746aedd6f 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -27,8 +27,10 @@ Rails.application.configure do config.cache_store = :null_store end + <%- unless skip_active_storage? -%> # Store uploaded files on the local file system (see config/storage.yml for options) config.active_storage.service = :local + <%- end -%> <%- unless options.skip_action_mailer? -%> # Don't care if the mailer can't send. diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 2e0b555f6f..4c0f36db98 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -44,9 +44,11 @@ Rails.application.configure do # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + <%- unless skip_active_storage? -%> # Store uploaded files on the local file system (see config/storage.yml for options) config.active_storage.service = :local + <%- end -%> <%- unless options[:skip_action_cable] -%> # Mount Action Cable outside main process or domain # config.action_cable.mount_path = nil diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt index a53978bc6e..a19f490d91 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -28,8 +28,11 @@ Rails.application.configure do # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false + <%- unless skip_active_storage? -%> # Store uploaded files on the local file system in a temporary directory config.active_storage.service = :test + + <%- end -%> <%- unless options.skip_action_mailer? -%> config.action_mailer.perform_caching = false diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore index 83a7b211aa..bd6b34346a 100644 --- a/railties/lib/rails/generators/rails/app/templates/gitignore +++ b/railties/lib/rails/generators/rails/app/templates/gitignore @@ -21,8 +21,10 @@ !/tmp/.keep <% end -%> +<% unless skip_active_storage? -%> # Ignore uploaded files in development /storage/* +<% end -%> <% unless options.skip_yarn? -%> /node_modules diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index d9ca4712d0..bc2dcf008e 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -87,7 +87,7 @@ task default: :test end PASSTHROUGH_OPTIONS = [ - :skip_active_record, :skip_action_mailer, :skip_javascript, :skip_action_cable, :skip_sprockets, :database, + :skip_active_record, :skip_active_storage, :skip_action_mailer, :skip_javascript, :skip_action_cable, :skip_sprockets, :database, :javascript, :skip_yarn, :api, :quiet, :pretend, :skip ] diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore index e15863d860..7a68da5c4b 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/gitignore +++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore @@ -11,5 +11,8 @@ pkg/ <%= dummy_path %>/node_modules/ <%= dummy_path %>/yarn-error.log <% end -%> +<% unless skip_active_storage? -%> +<%= dummy_path %>/storage/ +<% end -%> <%= dummy_path %>/tmp/ <% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb index 47b56ae3df..06ffe2f1ed 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb @@ -8,10 +8,10 @@ require "rails" require "active_model/railtie" require "active_job/railtie" <%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" require "action_controller/railtie" <%= comment_if :skip_action_mailer %>require "action_mailer/railtie" require "action_view/railtie" -require "active_storage/engine" <%= comment_if :skip_action_cable %>require "action_cable/engine" <%= comment_if :skip_sprockets %>require "sprockets/railtie" <%= comment_if :skip_test %>require "rails/test_unit/railtie" diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js index e54c6461cc..f3d80c87f5 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js @@ -10,4 +10,7 @@ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // +<% unless skip_active_storage? -%> +//= require activestorage +<% end -%> //= require_tree . diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 160ad1a928..0e2988952b 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -75,6 +75,7 @@ DEFAULT_APP_FILES = %w( log package.json public + storage test/application_system_test_case.rb test/test_helper.rb test/fixtures @@ -89,6 +90,7 @@ DEFAULT_APP_FILES = %w( tmp tmp/cache tmp/cache/assets + tmp/storage ) class AppGeneratorTest < Rails::Generators::TestCase @@ -296,6 +298,65 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_active_storage_mini_magick_gem + run_generator + assert_file "Gemfile", /^# gem 'mini_magick'/ + end + + def test_app_update_does_not_generate_active_storage_contents_when_skip_active_storage_is_given + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-active-storage"] + + FileUtils.cd(app_root) do + quietly { system("bin/rails app:update") } + end + + assert_file "#{app_root}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{app_root}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{app_root}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{app_root}/config/storage.yml" + + assert_file "#{app_root}/Gemfile" do |content| + assert_no_match(/gem 'mini_magick'/, content) + end + end + + def test_app_update_does_not_generate_active_storage_contents_when_skip_active_record_is_given + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-active-record"] + + FileUtils.cd(app_root) do + quietly { system("bin/rails app:update") } + end + + assert_file "#{app_root}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{app_root}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{app_root}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{app_root}/config/storage.yml" + + assert_file "#{app_root}/Gemfile" do |content| + assert_no_match(/gem 'mini_magick'/, content) + end + end + def test_application_names_are_not_singularized run_generator [File.join(destination_root, "hats")] assert_file "hats/config/environment.rb", /Rails\.application\.initialize!/ diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 38130ceb68..06d223e26d 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -230,7 +230,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase end def test_ensure_that_tests_work - run_generator + run_generator [destination_root, "--skip-active-storage"] FileUtils.cd destination_root quietly { system "bundle install" } assert_match(/1 runs, 1 assertions, 0 failures, 0 errors/, `bin/test 2>&1`) @@ -240,8 +240,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "--full", "--skip_active_record"] FileUtils.cd destination_root quietly { system "bundle install" } - # FIXME: Active Storage will provoke a test error without ActiveRecord (fix by allowing to skip active storage) - assert_match(/1 runs, 0 assertions, 0 failures, 1 errors/, `bundle exec rake test 2>&1`) + assert_match(/1 runs, 1 assertions, 0 failures, 0 errors/, `bundle exec rake test 2>&1`) end def test_ensure_that_migration_tasks_work_with_mountable_option diff --git a/railties/test/generators/plugin_test_runner_test.rb b/railties/test/generators/plugin_test_runner_test.rb index 89c3f1e496..48a3bcadcd 100644 --- a/railties/test/generators/plugin_test_runner_test.rb +++ b/railties/test/generators/plugin_test_runner_test.rb @@ -7,7 +7,7 @@ class PluginTestRunnerTest < ActiveSupport::TestCase def setup @destination_root = Dir.mktmpdir("bukkits") - Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --skip-bundle` } + Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --skip-bundle --skip-active-storage` } plugin_file "test/dummy/db/schema.rb", "" end diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index 654d16de65..cd86dd662f 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -189,6 +189,97 @@ module SharedGeneratorTests end end + def test_generator_for_active_storage + run_generator + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/storage.yml" + assert_directory "#{application_path}/db/migrate" + assert_directory "#{application_path}/storage" + assert_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_match(/\/storage\//, content) + end + end + + def test_generator_if_skip_active_storage_is_given + run_generator [destination_root, "--skip-active-storage"] + + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/ + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_no_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{application_path}/config/storage.yml" + assert_no_directory "#{application_path}/db/migrate" + assert_no_directory "#{application_path}/storage" + assert_no_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_no_match(/\/storage\//, content) + end + end + + def test_generator_does_not_generate_active_storage_contents_if_skip_active_record_is_given + run_generator [destination_root, "--skip-active-record"] + + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/ + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_no_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{application_path}/config/storage.yml" + assert_no_directory "#{application_path}/db/migrate" + assert_no_directory "#{application_path}/storage" + assert_no_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_no_match(/\/storage\//, content) + end + end + def test_generator_if_skip_action_mailer_is_given run_generator [destination_root, "--skip-action-mailer"] assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ -- cgit v1.2.3 From 0835527d6bb970cedd33633503506e41156ab780 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Mon, 7 Aug 2017 04:31:14 +0000 Subject: `rails new` runs `rails active_storage:install` Omit `rails activestorage:install` for jdbcmysql, jdbc and shebang tests AppGeneratorTest#test_config_jdbcmysql_database rails aborted! LoadError: Could not load 'active_record/connection_adapters/mysql_adapter'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile. (compressed) bin/rails:4:in `
' Tasks: TOP => activestorage:install => environment (See full trace by running task with --trace) AppGeneratorTest#test_config_jdbc_database rails aborted! LoadError: Could not load 'active_record/connection_adapters/jdbc_adapter'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile. (compressed) bin/rails:4:in `
' Tasks: TOP => activestorage:install => environment (See full trace by running task with --trace) AppGeneratorTest#test_shebang_is_added_to_rails_file /home/ubuntu/.rbenv/versions/2.4.1/bin/ruby: no Ruby script found in input (LoadError) Prevent PendingMigrationError in tests * Run `bin/rails db:migrate RAILS_ENV=test` in test_cases before start tests to prevent PendingMigrationError * FileUtils.rm_r("db/migrate") * --skip-active-storage Fix failed tests in `railties/test/railties/engine_test.rb` Related to #30111 Imporve `SharedGeneratorTests#test_default_frameworks_are_required_when_others_are_removed` - Explicitly skip active_storage - Ensure that skipped frameworks are commented - Ensure that default frameworks are not commented Fix error `Errno::ENOSPC: No space left on device - sendfile` Since `rails new` runs `rails active_storage:install` that boots an app. Since adding Bootsnap 0312a5c67e35b960e33677b5358c539f1047e4e1 during booting an app, it creates the cache: 264K tmp/cache/bootsnap-load-path-cache 27M tmp/cache/bootsnap-compile-cache * teardown_app must remove app --- .../test/support/integration/dummy_app_template.rb | 2 - activejob/test/support/integration/helper.rb | 1 + railties/lib/rails/generators/app_base.rb | 6 +++ .../rails/generators/rails/app/app_generator.rb | 1 + railties/test/application/bin_setup_test.rb | 4 ++ .../test/application/integration_test_case_test.rb | 7 +++- railties/test/application/rackup_test.rb | 1 + railties/test/application/rake/dbs_test.rb | 4 ++ railties/test/application/test_runner_test.rb | 5 +++ railties/test/application/test_test.rb | 5 +++ railties/test/generators/app_generator_test.rb | 13 +++--- .../scaffold_controller_generator_test.rb | 2 + railties/test/generators/shared_generator_tests.rb | 29 +++++++++---- .../test/generators/test_runner_in_engine_test.rb | 2 +- railties/test/isolation/abstract_unit.rb | 1 + railties/test/railties/engine_test.rb | 47 +++++++++++++--------- 16 files changed, 92 insertions(+), 38 deletions(-) diff --git a/activejob/test/support/integration/dummy_app_template.rb b/activejob/test/support/integration/dummy_app_template.rb index ac382bd1b7..7ea78c3350 100644 --- a/activejob/test/support/integration/dummy_app_template.rb +++ b/activejob/test/support/integration/dummy_app_template.rb @@ -4,8 +4,6 @@ if ENV["AJ_ADAPTER"] == "delayed_job" generate "delayed_job:active_record", "--quiet" end -rails_command("db:migrate") - initializer "activejob.rb", <<-CODE require "#{File.expand_path("jobs_manager.rb", __dir__)}" JobsManager.current_manager.setup diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb index a058da141f..a02d874e2e 100644 --- a/activejob/test/support/integration/helper.rb +++ b/activejob/test/support/integration/helper.rb @@ -18,6 +18,7 @@ Rails::Generators::AppGenerator.start args require "#{dummy_app_path}/config/environment.rb" ActiveRecord::Migrator.migrations_paths = [ Rails.root.join("db/migrate").to_s ] +ActiveRecord::Tasks::DatabaseTasks.migrate require "rails/test_help" Rails.backtrace_cleaner.remove_silencers! diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 2809c433f6..4f3ecd9189 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -459,6 +459,12 @@ module Rails end end + def run_active_storage + unless skip_active_storage? + rails_command "active_storage:install" + end + end + def empty_directory_with_keep_file(destination, config = {}) empty_directory(destination, config) keep_file(destination) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 15c5b973ca..1c32fad3ea 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -461,6 +461,7 @@ module Rails public_task :apply_rails_template, :run_bundle public_task :run_webpack, :generate_spring_binstubs + public_task :run_active_storage def run_after_bundle_callbacks @after_bundle_callbacks.each(&:call) diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb index 54934dbe24..9fac0435e4 100644 --- a/railties/test/application/bin_setup_test.rb +++ b/railties/test/application/bin_setup_test.rb @@ -16,6 +16,8 @@ module ApplicationTests def test_bin_setup Dir.chdir(app_path) do + FileUtils.rm_rf("db/migrate") + app_file "db/schema.rb", <<-RUBY ActiveRecord::Schema.define(version: 20140423102712) do create_table(:articles) {} @@ -37,6 +39,8 @@ module ApplicationTests def test_bin_setup_output Dir.chdir(app_path) do + FileUtils.rm_rf("db/migrate") + app_file "db/schema.rb", "" output = `bin/setup 2>&1` diff --git a/railties/test/application/integration_test_case_test.rb b/railties/test/application/integration_test_case_test.rb index 9edc907fce..c08761092b 100644 --- a/railties/test/application/integration_test_case_test.rb +++ b/railties/test/application/integration_test_case_test.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true require "isolation/abstract_unit" +require "env_helpers" module ApplicationTests class IntegrationTestCaseTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation + include ActiveSupport::Testing::Isolation, EnvHelpers setup do build_app @@ -39,13 +40,14 @@ module ApplicationTests end RUBY + with_rails_env("test") { rails("db:migrate") } output = rails("test") assert_match(/0 failures, 0 errors/, output) end end class IntegrationTestDefaultApp < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation + include ActiveSupport::Testing::Isolation, EnvHelpers setup do build_app @@ -66,6 +68,7 @@ module ApplicationTests end RUBY + with_rails_env("test") { rails("db:migrate") } output = rails("test") assert_match(/0 failures, 0 errors/, output) end diff --git a/railties/test/application/rackup_test.rb b/railties/test/application/rackup_test.rb index 383f18a7da..1dcc2826f0 100644 --- a/railties/test/application/rackup_test.rb +++ b/railties/test/application/rackup_test.rb @@ -26,6 +26,7 @@ module ApplicationTests test "config.ru can be racked up" do Dir.chdir app_path do + FileUtils.rm_rf("db/migrate") @app = rackup assert_welcome get("/") end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index fd22477539..009f2887c9 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -287,6 +287,8 @@ module ApplicationTests ENV.delete "RAILS_ENV" ENV.delete "RACK_ENV" + Dir.chdir(app_path) { FileUtils.rm_rf("db/migrate") } + app_file "db/schema.rb", <<-RUBY ActiveRecord::Schema.define(version: "1") do create_table :users do |t| @@ -308,6 +310,8 @@ module ApplicationTests end test "db:setup sets ar_internal_metadata" do + Dir.chdir(app_path) { FileUtils.rm_rf("db/migrate") } + app_file "db/schema.rb", "" rails "db:setup" diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index e92a0466dd..30bd283b48 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -11,6 +11,7 @@ module ApplicationTests def setup build_app create_schema + remove_migrations end def teardown @@ -727,6 +728,10 @@ module ApplicationTests app_file "db/schema.rb", "" end + def remove_migrations + Dir.chdir(app_path) { FileUtils.rm_rf("db/migrate") } + end + def create_test_file(path = :unit, name = "test", pass: true) app_file "test/#{path}/#{name}_test.rb", <<-RUBY require 'test_helper' diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb index 0a51e98656..50238ed682 100644 --- a/railties/test/application/test_test.rb +++ b/railties/test/application/test_test.rb @@ -8,6 +8,7 @@ module ApplicationTests def setup build_app + remove_migrations end def teardown @@ -320,6 +321,10 @@ Expected: ["id", "name"] end private + def remove_migrations + Dir.chdir(app_path) { FileUtils.rm_rf("db/migrate") } + end + def assert_unsuccessful_run(name, message) result = run_test_file(name) assert_not_equal 0, $?.to_i diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 0e2988952b..c5a59edab2 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -68,6 +68,7 @@ DEFAULT_APP_FILES = %w( config/spring.rb config/storage.yml db + db/migrate db/seeds.rb lib lib/tasks @@ -408,7 +409,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end def test_config_jdbcmysql_database - run_generator([destination_root, "-d", "jdbcmysql"]) + run_generator([destination_root, "-d", "jdbcmysql", "--skip-active-storage"]) assert_file "config/database.yml", /mysql/ assert_gem "activerecord-jdbcmysql-adapter" end @@ -426,7 +427,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end def test_config_jdbc_database - run_generator([destination_root, "-d", "jdbc"]) + run_generator([destination_root, "-d", "jdbc", "--skip-active-storage"]) assert_file "config/database.yml", /jdbc/ assert_file "config/database.yml", /mssql/ assert_gem "activerecord-jdbc-adapter" @@ -803,7 +804,7 @@ class AppGeneratorTest < Rails::Generators::TestCase template end - sequence = ["git init", "install", "exec spring binstub --all", "echo ran after_bundle"] + sequence = ["git init", "install", "exec spring binstub --all", "active_storage:install", "echo ran after_bundle"] @sequence_step ||= 0 ensure_bundler_first = -> command, options = nil do assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" @@ -813,12 +814,14 @@ class AppGeneratorTest < Rails::Generators::TestCase generator([destination_root], template: path).stub(:open, check_open, template) do generator.stub(:bundle_command, ensure_bundler_first) do generator.stub(:run, ensure_bundler_first) do - quietly { generator.invoke_all } + generator.stub(:rails_command, ensure_bundler_first) do + quietly { generator.invoke_all } + end end end end - assert_equal 4, @sequence_step + assert_equal 5, @sequence_step end def test_system_tests_directory_generated diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 513b037043..fd5aa817b4 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -207,6 +207,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase Dir.chdir(engine_path) do quietly { `bin/rails g controller dashboard foo` } + quietly { `bin/rails db:migrate RAILS_ENV=test` } assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`) end end @@ -218,6 +219,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase Dir.chdir(engine_path) do quietly { `bin/rails g controller dashboard foo` } + quietly { `bin/rails db:migrate RAILS_ENV=test` } assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`) end end diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index cd86dd662f..ed09847443 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -63,7 +63,7 @@ module SharedGeneratorTests end def test_shebang_is_added_to_rails_file - run_generator [destination_root, "--ruby", "foo/bar/baz", "--full"] + run_generator [destination_root, "--ruby", "foo/bar/baz", "--full", "--skip-active-storage"] assert_file "bin/rails", /#!foo\/bar\/baz/ end @@ -129,13 +129,26 @@ module SharedGeneratorTests end def test_default_frameworks_are_required_when_others_are_removed - run_generator [destination_root, "--skip-active-record", "--skip-action-mailer", "--skip-action-cable", "--skip-sprockets"] - assert_file "#{application_path}/config/application.rb", /require\s+["']rails["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']active_model\/railtie["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']active_job\/railtie["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']action_controller\/railtie["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']action_view\/railtie["']/ - assert_file "#{application_path}/config/application.rb", /require\s+["']active_storage\/engine["']/ + run_generator [ + destination_root, + "--skip-active-record", + "--skip-active-storage", + "--skip-action-mailer", + "--skip-action-cable", + "--skip-sprockets" + ] + + assert_file "#{application_path}/config/application.rb", /^require\s+["']rails["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']active_model\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']active_job\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']active_record\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']active_storage\/engine["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']action_controller\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_mailer\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']action_view\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_cable\/engine["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']sprockets\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']rails\/test_unit\/railtie["']/ end def test_generator_without_skips diff --git a/railties/test/generators/test_runner_in_engine_test.rb b/railties/test/generators/test_runner_in_engine_test.rb index 0e15b5e388..2acd96ecd4 100644 --- a/railties/test/generators/test_runner_in_engine_test.rb +++ b/railties/test/generators/test_runner_in_engine_test.rb @@ -7,7 +7,7 @@ class TestRunnerInEngineTest < ActiveSupport::TestCase def setup @destination_root = Dir.mktmpdir("bukkits") - Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --full --skip-bundle` } + Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --full --skip-bundle --skip-active-storage` } plugin_file "test/dummy/db/schema.rb", "" end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 29daaacdb2..7522237a38 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -153,6 +153,7 @@ module TestHelpers def teardown_app ENV["RAILS_ENV"] = @prev_rails_env if @prev_rails_env + FileUtils.rm_rf(tmp_path) end # Make a very basic app, without creating the whole directory structure. diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index e6964b4b18..132b3b3a6e 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -32,6 +32,11 @@ module RailtiesTest require "#{app_path}/config/environment" end + def migrations + migration_root = File.expand_path(ActiveRecord::Migrator.migrations_paths.first, app_path) + ActiveRecord::Migrator.migrations(migration_root) + end + test "serving sprocket's assets" do @plugin.write "app/assets/javascripts/engine.js.erb", "<%= :alert %>();" add_to_env_config "development", "config.assets.digest = false" @@ -82,31 +87,30 @@ module RailtiesTest end RUBY - add_to_config "ActiveRecord::Base.timestamped_migrations = false" - boot_rails Dir.chdir(app_path) do output = `bundle exec rake bukkits:install:migrations` - assert File.exist?("#{app_path}/db/migrate/2_create_users.bukkits.rb") - assert File.exist?("#{app_path}/db/migrate/3_add_last_name_to_users.bukkits.rb") - assert_match(/Copied migration 2_create_users\.bukkits\.rb from bukkits/, output) - assert_match(/Copied migration 3_add_last_name_to_users\.bukkits\.rb from bukkits/, output) - assert_match(/NOTE: Migration 3_create_sessions\.rb from bukkits has been skipped/, output) - assert_equal 3, Dir["#{app_path}/db/migrate/*.rb"].length + ["CreateUsers", "AddLastNameToUsers", "CreateSessions"].each do |migration_name| + assert migrations.detect { |migration| migration.name == migration_name } + end + assert_match(/Copied migration \d+_create_users\.bukkits\.rb from bukkits/, output) + assert_match(/Copied migration \d+_add_last_name_to_users\.bukkits\.rb from bukkits/, output) + assert_match(/NOTE: Migration \d+_create_sessions\.rb from bukkits has been skipped/, output) - output = `bundle exec rake railties:install:migrations`.split("\n") + migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length - assert_no_match(/2_create_users/, output.join("\n")) + assert_equal migrations.length, migrations_count - bukkits_migration_order = output.index(output.detect { |o| /NOTE: Migration 3_create_sessions\.rb from bukkits has been skipped/ =~ o }) - assert_not_nil bukkits_migration_order, "Expected migration to be skipped" - - migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length - `bundle exec rake railties:install:migrations` + output = `bundle exec rake railties:install:migrations`.split("\n") assert_equal migrations_count, Dir["#{app_path}/db/migrate/*.rb"].length + + assert_no_match(/\d+_create_users/, output.join("\n")) + + bukkits_migration_order = output.index(output.detect { |o| /NOTE: Migration \d+_create_sessions\.rb from bukkits has been skipped/ =~ o }) + assert_not_nil bukkits_migration_order, "Expected migration to be skipped" end end @@ -173,8 +177,8 @@ module RailtiesTest Dir.chdir(app_path) do output = `bundle exec rake railties:install:migrations`.split("\n") - assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.second) - assert_match(/Copied migration \d+_create_keys\.api_engine\.rb from api_engine/, output.last) + assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.first) + assert_match(/Copied migration \d+_create_keys\.api_engine\.rb from api_engine/, output.second) end end @@ -203,9 +207,12 @@ module RailtiesTest Dir.chdir(@plugin.path) do output = `bundle exec rake app:bukkits:install:migrations` - assert File.exist?("#{app_path}/db/migrate/0_add_first_name_to_users.bukkits.rb") - assert_match(/Copied migration 0_add_first_name_to_users\.bukkits\.rb from bukkits/, output) - assert_equal 1, Dir["#{app_path}/db/migrate/*.rb"].length + + migration_with_engine_path = migrations.detect { |migration| migration.name == "AddFirstNameToUsers" } + assert migration_with_engine_path + assert_match(/\/db\/migrate\/\d+_add_first_name_to_users\.bukkits\.rb/, migration_with_engine_path.filename) + assert_match(/Copied migration \d+_add_first_name_to_users\.bukkits\.rb from bukkits/, output) + assert_equal migrations.length, Dir["#{app_path}/db/migrate/*.rb"].length end end -- cgit v1.2.3 From f4af77ab5d6f5bd3b045f676978bc2a4677cee38 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 11 Oct 2017 10:08:10 +0300 Subject: Rails::Generators::Actions#execute_command allows option `capture` --- railties/lib/rails/generators/actions.rb | 8 +++++++- railties/test/generators/actions_test.rb | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 9800e5750a..3362bf629a 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -221,6 +221,7 @@ module Rails # rake("db:migrate") # rake("db:migrate", env: "production") # rake("gems:install", sudo: true) + # rake("gems:install", capture: true) def rake(command, options = {}) execute_command :rake, command, options end @@ -230,6 +231,7 @@ module Rails # rails_command("db:migrate") # rails_command("db:migrate", env: "production") # rails_command("gems:install", sudo: true) + # rails_command("gems:install", capture: true) def rails_command(command, options = {}) execute_command :rails, command, options end @@ -292,7 +294,11 @@ module Rails log executor, command env = options[:env] || ENV["RAILS_ENV"] || "development" sudo = options[:sudo] && !Gem.win_platform? ? "sudo " : "" - in_root { run("#{sudo}#{extify(executor)} #{command} RAILS_ENV=#{env}", verbose: false) } + config = { verbose: false } + + config.merge!(capture: options[:capture]) if options[:capture] + + in_root { run("#{sudo}#{extify(executor)} #{command} RAILS_ENV=#{env}", config) } end # Add an extension to the given name based on the platform. diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 3ecfb4edd9..f421207025 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -308,6 +308,14 @@ class ActionsTest < Rails::Generators::TestCase end end + test "rake command with capture option should run rake command with capture" do + assert_called_with(generator, :run, ["rake log:clear RAILS_ENV=development", verbose: false, capture: true]) do + with_rails_env nil do + action :rake, "log:clear", capture: true + end + end + end + test "rails command should run rails_command with default env" do assert_called_with(generator, :run, ["rails log:clear RAILS_ENV=development", verbose: false]) do with_rails_env nil do @@ -346,6 +354,14 @@ class ActionsTest < Rails::Generators::TestCase end end + test "rails command with capture option should run rails_command with capture" do + assert_called_with(generator, :run, ["rails log:clear RAILS_ENV=development", verbose: false, capture: true]) do + with_rails_env nil do + action :rails_command, "log:clear", capture: true + end + end + end + def test_capify_should_run_the_capify_command content = capture(:stderr) do assert_called_with(generator, :run, ["capify .", verbose: false]) do -- cgit v1.2.3 From cb8553c95d345e40b448cf8e10aa033ff0ede4f2 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 11 Oct 2017 10:11:35 +0300 Subject: Execution of `active_storage:install` should respect `--quiet` during `rails new` --- railties/lib/rails/generators/app_base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 4f3ecd9189..59ca4c849f 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -461,7 +461,7 @@ module Rails def run_active_storage unless skip_active_storage? - rails_command "active_storage:install" + rails_command "active_storage:install", capture: options[:quiet] end end -- cgit v1.2.3 From 165d5b601d44495452dcebba5a61b31b3be36da7 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Tue, 7 Nov 2017 07:35:54 +0900 Subject: Fix merge conflict and rubocop offences --- .../test/dispatch/routing_assertions_test.rb | 37 +++++++++++----------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb index a8f00af6de..a5198f2f13 100644 --- a/actionpack/test/dispatch/routing_assertions_test.rb +++ b/actionpack/test/dispatch/routing_assertions_test.rb @@ -1,4 +1,3 @@ -<<<<<<< HEAD # frozen_string_literal: true require "abstract_unit" @@ -23,16 +22,16 @@ class RoutingAssertionsTest < ActionController::TestCase engine.routes.draw do resources :books - scope 'secure', :constraints => { :protocol => 'https://' } do - resources :books, :controller => 'secure_books' + scope "secure", constraints: { protocol: "https://" } do + resources :books, controller: "secure_books" end - scope 'block', :constraints => lambda { |r| r.ssl? } do - resources :books, :controller => 'block_books' + scope "block", constraints: lambda { |r| r.ssl? } do + resources :books, controller: "block_books" end - scope 'query', :constraints => lambda { |r| r.params[:use_query] == 'true' } do - resources :books, :controller => 'query_books' + scope "query", constraints: lambda { |r| r.params[:use_query] == "true" } do + resources :books, controller: "query_books" end end @@ -113,43 +112,43 @@ class RoutingAssertionsTest < ActionController::TestCase end def test_assert_recognizes_with_engine - assert_recognizes({ :controller => 'books', :action => 'index' }, '/shelf/books') - assert_recognizes({ :controller => 'books', :action => 'show', :id => '1' }, '/shelf/books/1') + assert_recognizes({ controller: "books", action: "index" }, "/shelf/books") + assert_recognizes({ controller: "books", action: "show", id: "1" }, "/shelf/books/1") end def test_assert_recognizes_with_engine_and_extras - assert_recognizes({ :controller => 'books', :action => 'index', :page => '1' }, '/shelf/books', { :page => '1' }) + assert_recognizes({ controller: "books", action: "index", page: "1" }, "/shelf/books", page: "1") end def test_assert_recognizes_with_engine_and_method - assert_recognizes({ :controller => 'books', :action => 'create' }, { :path => '/shelf/books', :method => :post }) - assert_recognizes({ :controller => 'books', :action => 'update', :id => '1' }, { :path => '/shelf/books/1', :method => :put }) + assert_recognizes({ controller: "books", action: "create" }, { path: "/shelf/books", method: :post }) + assert_recognizes({ controller: "books", action: "update", id: "1" }, { path: "/shelf/books/1", method: :put }) end def test_assert_recognizes_with_engine_and_hash_constraint assert_raise(Assertion) do - assert_recognizes({ :controller => 'secure_books', :action => 'index' }, 'http://test.host/shelf/secure/books') + assert_recognizes({ controller: "secure_books", action: "index" }, "http://test.host/shelf/secure/books") end - assert_recognizes({ :controller => 'secure_books', :action => 'index', :protocol => 'https://' }, 'https://test.host/shelf/secure/books') + assert_recognizes({ controller: "secure_books", action: "index", protocol: "https://" }, "https://test.host/shelf/secure/books") end def test_assert_recognizes_with_engine_and_block_constraint assert_raise(Assertion) do - assert_recognizes({ :controller => 'block_books', :action => 'index' }, 'http://test.host/shelf/block/books') + assert_recognizes({ controller: "block_books", action: "index" }, "http://test.host/shelf/block/books") end - assert_recognizes({ :controller => 'block_books', :action => 'index' }, 'https://test.host/shelf/block/books') + assert_recognizes({ controller: "block_books", action: "index" }, "https://test.host/shelf/block/books") end def test_assert_recognizes_with_engine_and_query_constraint assert_raise(Assertion) do - assert_recognizes({ :controller => 'query_books', :action => 'index', :use_query => 'false' }, '/shelf/query/books', { :use_query => 'false' }) + assert_recognizes({ controller: "query_books", action: "index", use_query: "false" }, "/shelf/query/books", use_query: "false") end - assert_recognizes({ :controller => 'query_books', :action => 'index', :use_query => 'true' }, '/shelf/query/books', { :use_query => 'true' }) + assert_recognizes({ controller: "query_books", action: "index", use_query: "true" }, "/shelf/query/books", use_query: "true") end def test_assert_recognizes_raises_message_with_engine err = assert_raise(Assertion) do - assert_recognizes({ :controller => 'secure_books', :action => 'index' }, 'http://test.host/shelf/secure/books', {}, "This is a really bad msg") + assert_recognizes({ controller: "secure_books", action: "index" }, "http://test.host/shelf/secure/books", {}, "This is a really bad msg") end assert_match err.message, "This is a really bad msg" -- cgit v1.2.3 From 90fe2a42f0b68a66e970169d38e91a0126de7d3e Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Tue, 26 Sep 2017 00:10:17 +0300 Subject: Fix `bin/rails db:migrate` with specified `VERSION` Ensure that `bin/rails db:migrate` with specified `VERSION` reverts all migrations only if `VERSION` is `0`. Raise error if target migration doesn't exist. --- activerecord/CHANGELOG.md | 8 ++ activerecord/lib/active_record/migration.rb | 2 +- .../lib/active_record/railties/databases.rake | 19 ++- .../lib/active_record/tasks/database_tasks.rb | 15 +- activerecord/test/cases/migrator_test.rb | 20 +++ .../test/cases/tasks/database_tasks_test.rb | 149 +++++++++++++++++++- railties/test/application/rake/migrations_test.rb | 156 ++++++++++++++++++++- 7 files changed, 355 insertions(+), 14 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 4aa7ecfc7e..e6a41ec281 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,11 @@ +* Fix `bin/rails db:migrate` with specified `VERSION`. + `bin/rails db:migrate` with empty VERSION behaves as without `VERSION`. + Check a format of `VERSION`: Allow a migration version number + or name of a migration file. Raise error if format of `VERSION` is invalid. + Raise error if target migration doesn't exist. + + *bogdanvlviv* + * Fixed a bug where column orders for an index weren't written to db/schema.rb when using the sqlite adapter. diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 1485687053..d12a979a7f 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1224,7 +1224,7 @@ module ActiveRecord # Return true if a valid version is not provided. def invalid_target? - !target && @target_version && @target_version > 0 + @target_version && @target_version != 0 && !target end def execute_migration_in_transaction(migration, direction) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 723272b4b2..3bca2982e0 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -97,16 +97,27 @@ db_namespace = namespace :db do task up: [:environment, :load_config] do raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty? - version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil - ActiveRecord::Migrator.run(:up, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version) + ActiveRecord::Tasks::DatabaseTasks.check_target_version + + ActiveRecord::Migrator.run( + :up, + ActiveRecord::Tasks::DatabaseTasks.migrations_paths, + ActiveRecord::Tasks::DatabaseTasks.target_version + ) db_namespace["_dump"].invoke end # desc 'Runs the "down" for a given migration VERSION.' task down: [:environment, :load_config] do raise "VERSION is required - To go down one migration, use db:rollback" if !ENV["VERSION"] || ENV["VERSION"].empty? - version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil - ActiveRecord::Migrator.run(:down, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version) + + ActiveRecord::Tasks::DatabaseTasks.check_target_version + + ActiveRecord::Migrator.run( + :down, + ActiveRecord::Tasks::DatabaseTasks.migrations_paths, + ActiveRecord::Tasks::DatabaseTasks.target_version + ) db_namespace["_dump"].invoke end diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index ff388ff1f6..4657e51e6d 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -164,13 +164,12 @@ module ActiveRecord end def migrate - raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty? + check_target_version verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true - version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil scope = ENV["SCOPE"] verbose_was, Migration.verbose = Migration.verbose, verbose - Migrator.migrate(migrations_paths, version) do |migration| + Migrator.migrate(migrations_paths, target_version) do |migration| scope.blank? || scope == migration.scope end ActiveRecord::Base.clear_cache! @@ -178,6 +177,16 @@ module ActiveRecord Migration.verbose = verbose_was end + def check_target_version + if target_version && !(Migration::MigrationFilenameRegexp.match?(ENV["VERSION"]) || /\A\d+\z/.match?(ENV["VERSION"])) + raise "Invalid format of target version: `VERSION=#{ENV['VERSION']}`" + end + end + + def target_version + ENV["VERSION"].to_i if ENV["VERSION"] && !ENV["VERSION"].empty? + end + def charset_current(environment = env) charset ActiveRecord::Base.configurations[environment] end diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index ee10be119c..1047ba1367 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -66,6 +66,26 @@ class MigratorTest < ActiveRecord::TestCase list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)] ActiveRecord::Migrator.new(:up, list, 3).run end + + assert_raises(ActiveRecord::UnknownMigrationVersionError) do + list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)] + ActiveRecord::Migrator.new(:up, list, -1).run + end + + assert_raises(ActiveRecord::UnknownMigrationVersionError) do + list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)] + ActiveRecord::Migrator.new(:up, list, 0).run + end + + assert_raises(ActiveRecord::UnknownMigrationVersionError) do + list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)] + ActiveRecord::Migrator.new(:up, list, 3).migrate + end + + assert_raises(ActiveRecord::UnknownMigrationVersionError) do + list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)] + ActiveRecord::Migrator.new(:up, list, -1).migrate + end end def test_finds_migrations diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb index 1495d2ab89..5a094ead42 100644 --- a/activerecord/test/cases/tasks/database_tasks_test.rb +++ b/activerecord/test/cases/tasks/database_tasks_test.rb @@ -357,8 +357,15 @@ module ActiveRecord ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose) ActiveRecord::Tasks::DatabaseTasks.migrate + ENV["VERBOSE"] = "" + ENV["VERSION"] = "" + ActiveRecord::Migrator.expects(:migrate).with("custom/path", nil) + ActiveRecord::Migration.expects(:verbose=).with(true) + ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose) + ActiveRecord::Tasks::DatabaseTasks.migrate + ENV["VERBOSE"] = "yes" - ENV["VERSION"] = "unknown" + ENV["VERSION"] = "0" ActiveRecord::Migrator.expects(:migrate).with("custom/path", 0) ActiveRecord::Migration.expects(:verbose=).with(true) ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose) @@ -367,15 +374,47 @@ module ActiveRecord ENV["VERBOSE"], ENV["VERSION"] = verbose, version end - def test_migrate_raise_error_on_empty_version + def test_migrate_raise_error_on_invalid_version_format version = ENV["VERSION"] - ENV["VERSION"] = "" + + ENV["VERSION"] = "unknown" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "0.1.11" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "1.1.11" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "0 " + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "1." + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "1_" e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } - assert_equal "Empty VERSION provided", e.message + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "1_name" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } + assert_match(/Invalid format of target version/, e.message) ensure ENV["VERSION"] = version end + def test_migrate_raise_error_on_failed_check_target_version + ActiveRecord::Tasks::DatabaseTasks.stubs(:check_target_version).raises("foo") + + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } + assert_equal "foo", e.message + end + def test_migrate_clears_schema_cache_afterward ActiveRecord::Base.expects(:clear_cache!) ActiveRecord::Tasks::DatabaseTasks.migrate @@ -444,6 +483,108 @@ module ActiveRecord end end + class DatabaseTaskTargetVersionTest < ActiveRecord::TestCase + def test_target_version_returns_nil_if_version_does_not_exist + version = ENV.delete("VERSION") + assert_nil ActiveRecord::Tasks::DatabaseTasks.target_version + ensure + ENV["VERSION"] = version + end + + def test_target_version_returns_nil_if_version_is_empty + version = ENV["VERSION"] + + ENV["VERSION"] = "" + assert_nil ActiveRecord::Tasks::DatabaseTasks.target_version + ensure + ENV["VERSION"] = version + end + + def test_target_version_returns_converted_to_integer_env_version_if_version_exists + version = ENV["VERSION"] + + ENV["VERSION"] = "0" + assert_equal ENV["VERSION"].to_i, ActiveRecord::Tasks::DatabaseTasks.target_version + + ENV["VERSION"] = "42" + assert_equal ENV["VERSION"].to_i, ActiveRecord::Tasks::DatabaseTasks.target_version + + ENV["VERSION"] = "042" + assert_equal ENV["VERSION"].to_i, ActiveRecord::Tasks::DatabaseTasks.target_version + ensure + ENV["VERSION"] = version + end + end + + class DatabaseTaskCheckTargetVersionTest < ActiveRecord::TestCase + def test_check_target_version_does_not_raise_error_on_empty_version + version = ENV["VERSION"] + ENV["VERSION"] = "" + assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + ensure + ENV["VERSION"] = version + end + + def test_check_target_version_does_not_raise_error_if_version_is_not_setted + version = ENV.delete("VERSION") + assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + ensure + ENV["VERSION"] = version + end + + def test_check_target_version_raises_error_on_invalid_version_format + version = ENV["VERSION"] + + ENV["VERSION"] = "unknown" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "0.1.11" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "1.1.11" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "0 " + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "1." + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "1_" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + assert_match(/Invalid format of target version/, e.message) + + ENV["VERSION"] = "1_name" + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + assert_match(/Invalid format of target version/, e.message) + ensure + ENV["VERSION"] = version + end + + def test_check_target_version_does_not_raise_error_on_valid_version_format + version = ENV["VERSION"] + + ENV["VERSION"] = "0" + assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + + ENV["VERSION"] = "1" + assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + + ENV["VERSION"] = "001" + assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + + ENV["VERSION"] = "001_name.rb" + assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version } + ensure + ENV["VERSION"] = version + end + end + class DatabaseTasksStructureDumpTest < ActiveRecord::TestCase include DatabaseTasksSetupper diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index 33f2b7038c..788f9160d6 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -37,9 +37,64 @@ module ApplicationTests assert_match(/AMigration: reverted/, output) end + test "migrate with specified VERSION in different formats" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/03_three_migration.rb", <<-MIGRATION + class ThreeMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + assert_match(/up\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=01_one_migration.rb" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) + assert_match(/down\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=3" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + assert_match(/up\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=001" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) + assert_match(/down\s+003\s+Three migration/, output) + end + test "migration with empty version" do - output = rails("db:migrate", "VERSION=", allow_failure: true) - assert_match(/Empty VERSION provided/, output) + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails("db:migrate", "VERSION=") + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) output = rails("db:migrate:redo", "VERSION=", allow_failure: true) assert_match(/Empty VERSION provided/, output) @@ -55,6 +110,34 @@ module ApplicationTests output = rails("db:migrate:down", allow_failure: true) assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + end + + test "migration with 0 version" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + + rails "db:migrate", "VERSION=0" + + output = rails("db:migrate:status") + assert_match(/down\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) end test "model and migration generator with change syntax" do @@ -192,6 +275,75 @@ module ApplicationTests end end + test "raise error on any move when target migration does not exist" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + + output = rails("db:migrate", "VERSION=3", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output) + assert_match(/No migration with version number 3/, output) + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + end + + test "raise error on any move when VERSION has invalid format" do + output = rails("db:migrate", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=0.1.11", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1.1.11", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION='0 '", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1.", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1_", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1_name", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:redo", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:up", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:down", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + end + test "migration status after rollback and redo without timestamps" do add_to_config("config.active_record.timestamped_migrations = false") -- cgit v1.2.3 From 3ff2c158372bce45ea46bd3b49c49022a4259aba Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Sun, 5 Nov 2017 22:25:37 +0000 Subject: Add cases to test combining validation conditions - Test condition that is defined by array of conditions - Test condition that is defined by combining :if and :unless - Test local condition that is defined by :if - Test local condition that is defined by :unless See http://edgeguides.rubyonrails.org/active_record_validations.html#combining-validation-conditions --- .../validations/conditional_validation_test.rb | 49 +++++++++++++++++++++- .../validations/numericality_validation_test.rb | 2 +- .../test/cases/validations/validates_test.rb | 12 ++++-- activemodel/test/models/person.rb | 4 ++ activemodel/test/models/topic.rb | 2 +- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb index aa027c4128..caea8b65ef 100644 --- a/activemodel/test/cases/validations/conditional_validation_test.rb +++ b/activemodel/test/cases/validations/conditional_validation_test.rb @@ -18,6 +18,22 @@ class ConditionalValidationTest < ActiveModel::TestCase assert_equal ["hoo 5"], t.errors["title"] end + def test_if_validation_using_array_of_true_methods + Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: [:condition_is_true, :condition_is_true]) + t = Topic.new("title" => "uhohuhoh", "content" => "whatever") + assert t.invalid? + assert t.errors[:title].any? + assert_equal ["hoo 5"], t.errors["title"] + end + + def test_unless_validation_using_array_of_false_methods + Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: [:condition_is_false, :condition_is_false]) + t = Topic.new("title" => "uhohuhoh", "content" => "whatever") + assert t.invalid? + assert t.errors[:title].any? + assert_equal ["hoo 5"], t.errors["title"] + end + def test_unless_validation_using_method_true # When the method returns true Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: :condition_is_true) @@ -26,9 +42,23 @@ class ConditionalValidationTest < ActiveModel::TestCase assert_empty t.errors[:title] end + def test_if_validation_using_array_of_true_and_false_methods + Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: [:condition_is_true, :condition_is_false]) + t = Topic.new("title" => "uhohuhoh", "content" => "whatever") + assert t.valid? + assert_empty t.errors[:title] + end + + def test_unless_validation_using_array_of_true_and_felse_methods + Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: [:condition_is_true, :condition_is_false]) + t = Topic.new("title" => "uhohuhoh", "content" => "whatever") + assert t.valid? + assert_empty t.errors[:title] + end + def test_if_validation_using_method_false # When the method returns false - Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: :condition_is_true_but_its_not) + Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: :condition_is_false) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert_empty t.errors[:title] @@ -36,7 +66,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_method_false # When the method returns false - Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: :condition_is_true_but_its_not) + Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: :condition_is_false) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -80,4 +110,19 @@ class ConditionalValidationTest < ActiveModel::TestCase assert t.errors[:title].any? assert_equal ["hoo 5"], t.errors["title"] end + + def test_validation_using_conbining_if_true_and_unless_true_conditions + Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: :condition_is_true, unless: :condition_is_true) + t = Topic.new("title" => "uhohuhoh", "content" => "whatever") + assert t.valid? + assert_empty t.errors[:title] + end + + def test_validation_using_conbining_if_true_and_unless_false_conditions + Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: :condition_is_true, unless: :condition_is_false) + t = Topic.new("title" => "uhohuhoh", "content" => "whatever") + assert t.invalid? + assert t.errors[:title].any? + assert_equal ["hoo 5"], t.errors["title"] + end end diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index 001815e28f..fbbaf8a30c 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -59,7 +59,7 @@ class NumericalityValidationTest < ActiveModel::TestCase end def test_validates_numericality_of_with_integer_only_and_symbol_as_value - Topic.validates_numericality_of :approved, only_integer: :condition_is_true_but_its_not + Topic.validates_numericality_of :approved, only_integer: :condition_is_false invalid!(NIL + BLANK + JUNK) valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY) diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 77cb8ebdc1..7f32f5dc74 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -62,17 +62,23 @@ class ValidatesTest < ActiveModel::TestCase end def test_validates_with_if_as_local_conditions - Person.validates :karma, presence: true, email: { unless: :condition_is_true } + Person.validates :karma, presence: true, email: { if: :condition_is_false } person = Person.new person.valid? assert_equal ["can't be blank"], person.errors[:karma] end def test_validates_with_if_as_shared_conditions - Person.validates :karma, presence: true, email: true, if: :condition_is_true + Person.validates :karma, presence: true, email: true, if: :condition_is_false + person = Person.new + assert person.valid? + end + + def test_validates_with_unless_as_local_conditions + Person.validates :karma, presence: true, email: { unless: :condition_is_true } person = Person.new person.valid? - assert_equal ["can't be blank", "is not an email"], person.errors[:karma].sort + assert_equal ["can't be blank"], person.errors[:karma] end def test_validates_with_unless_shared_conditions diff --git a/activemodel/test/models/person.rb b/activemodel/test/models/person.rb index b61fdf76b1..8dd8ceadad 100644 --- a/activemodel/test/models/person.rb +++ b/activemodel/test/models/person.rb @@ -9,6 +9,10 @@ class Person def condition_is_true true end + + def condition_is_false + false + end end class Person::Gender diff --git a/activemodel/test/models/topic.rb b/activemodel/test/models/topic.rb index 2f4e92c3b2..b0af00ee45 100644 --- a/activemodel/test/models/topic.rb +++ b/activemodel/test/models/topic.rb @@ -23,7 +23,7 @@ class Topic true end - def condition_is_true_but_its_not + def condition_is_false false end -- cgit v1.2.3 From 3f1695bb9c008b7cb1840e09e640f3ec0c59a564 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Tue, 7 Nov 2017 08:44:44 +0900 Subject: Remove useless `associated_records_by_owner` `associated_records_by_owner` had returned customizing result before calling `associate_records_to_owner` for through association subclasses. Since #22115, `associate_records_to_owner` is called in the method and not returned owner and result pairs. Removing the method will reduce method call and block call nesting. --- .../associations/preloader/association.rb | 22 ++++++++-------------- .../associations/preloader/through_association.rb | 4 ++-- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index b0c41c7d5f..e77761692d 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -17,8 +17,14 @@ module ActiveRecord end def run(preloader) - associated_records_by_owner(preloader) do |owner, records| - associate_records_to_owner(owner, records) + records = load_records do |record| + owner = owners_by_key[convert_key(record[association_key_name])] + association = owner.association(reflection.name) + association.set_inverse_instance(record) + end + + owners.each do |owner| + associate_records_to_owner(owner, records[convert_key(owner[owner_key_name])] || []) end end @@ -33,18 +39,6 @@ module ActiveRecord reflection.join_foreign_key end - def associated_records_by_owner(preloader) - records = load_records do |record| - owner = owners_by_key[convert_key(record[association_key_name])] - association = owner.association(reflection.name) - association.set_inverse_instance(record) - end - - owners.each_with_object({}) do |owner, result| - yield(owner, records[convert_key(owner[owner_key_name])] || []) - end - end - def associate_records_to_owner(owner, records) raise NotImplementedError end diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index 1bb7e98231..b1813ba66b 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -12,7 +12,7 @@ module ActiveRecord reflection.source_reflection end - def associated_records_by_owner(preloader) + def run(preloader) already_loaded = owners.first.association(through_reflection.name).loaded? through_scope = through_scope() reflection_scope = target_reflection_scope @@ -43,7 +43,7 @@ module ActiveRecord result.sort_by! { |rhs| preload_index[rhs] } if reflection_scope.order_values.any? result.uniq! if reflection_scope.distinct_value end - yield(owner, result) + associate_records_to_owner(owner, result) end end -- cgit v1.2.3 From 86938c495e282e6a61c16a9e1d77582e22c0a4fc Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Mon, 6 Nov 2017 21:29:37 -0500 Subject: Fix streaming downloads from S3/Azure Storage Closes #31073. --- .../lib/active_storage/service/azure_storage_service.rb | 8 ++++---- activestorage/lib/active_storage/service/s3_service.rb | 6 +++--- activestorage/test/service/shared_service_tests.rb | 10 ++++++++++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb index 27dd192ce6..f3877ad9c9 100644 --- a/activestorage/lib/active_storage/service/azure_storage_service.rb +++ b/activestorage/lib/active_storage/service/azure_storage_service.rb @@ -28,7 +28,7 @@ module ActiveStorage end end - def download(key) + def download(key, &block) if block_given? instrument :streaming_download, key do stream(key, &block) @@ -108,15 +108,15 @@ module ActiveStorage end # Reads the object for the given key in chunks, yielding each to the block. - def stream(key, options = {}, &block) + def stream(key) blob = blob_for(key) chunk_size = 5.megabytes offset = 0 while offset < blob.properties[:content_length] - _, io = blobs.get_blob(container, key, start_range: offset, end_range: offset + chunk_size - 1) - yield io + _, chunk = blobs.get_blob(container, key, start_range: offset, end_range: offset + chunk_size - 1) + yield chunk.force_encoding(Encoding::BINARY) offset += chunk_size end end diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb index 3e93cdd072..958b172efb 100644 --- a/activestorage/lib/active_storage/service/s3_service.rb +++ b/activestorage/lib/active_storage/service/s3_service.rb @@ -26,7 +26,7 @@ module ActiveStorage end end - def download(key) + def download(key, &block) if block_given? instrument :streaming_download, key do stream(key, &block) @@ -85,14 +85,14 @@ module ActiveStorage end # Reads the object for the given key in chunks, yielding each to the block. - def stream(key, options = {}, &block) + def stream(key, &block) object = object_for(key) chunk_size = 5.megabytes offset = 0 while offset < object.content_length - yield object.read(options.merge(range: "bytes=#{offset}-#{offset + chunk_size - 1}")) + yield object.get(range: "bytes=#{offset}-#{offset + chunk_size - 1}").body.read.force_encoding(Encoding::BINARY) offset += chunk_size end end diff --git a/activestorage/test/service/shared_service_tests.rb b/activestorage/test/service/shared_service_tests.rb index a9e1cb6ce9..ade91ab89a 100644 --- a/activestorage/test/service/shared_service_tests.rb +++ b/activestorage/test/service/shared_service_tests.rb @@ -50,6 +50,16 @@ module ActiveStorage::Service::SharedServiceTests assert_equal FIXTURE_DATA, @service.download(FIXTURE_KEY) end + test "downloading in chunks" do + chunks = [] + + @service.download(FIXTURE_KEY) do |chunk| + chunks << chunk + end + + assert_equal [ FIXTURE_DATA ], chunks + end + test "existing" do assert @service.exist?(FIXTURE_KEY) assert_not @service.exist?(FIXTURE_KEY + "nonsense") -- cgit v1.2.3 From 38158451103be572d6e81e971797938df7bd22ea Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Tue, 7 Nov 2017 14:23:16 +0900 Subject: Fix comment in `check_class_collision` [ci skip] `ScaffoldBase` was changed to `ResourceHelpers` by 0efedf2. --- railties/lib/rails/generators/named_base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 60625283ac..99165168fd 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -221,7 +221,7 @@ module Rails # def self.check_class_collision(options = {}) # :doc: define_method :check_class_collision do - name = if respond_to?(:controller_class_name) # for ScaffoldBase + name = if respond_to?(:controller_class_name) # for ResourceHelpers controller_class_name else class_name -- cgit v1.2.3 From bbe437faecca5fd6bdc2327a4bc7a31ba21afe2e Mon Sep 17 00:00:00 2001 From: Genadi Samokovarov Date: Tue, 7 Nov 2017 10:28:19 +0200 Subject: Use plain assert in assert_changes to avoid MT6 refutes Seeing the previously issued PRs about it, we can avoid the `nil` comparisons that can happen in `assert_changes` by using plain `assert` calls. This is to avoid a deprecation warning about comparing `nil` values in `assert_equal` for Minitest 5 and a crash in Minitest 6. You can see the preparations done in [`assert_equal`][ae]. You can also see that [`assert`][a] does not care about `nil`s. [ae]: https://github.com/seattlerb/minitest/blob/ca6a71ca901016db09a5ad466b4adea4b52a504a/lib/minitest/assertions.rb#L159-L188 [a]: https://github.com/seattlerb/minitest/blob/ca6a71ca901016db09a5ad466b4adea4b52a504a/lib/minitest/assertions.rb#L131-L142 --- activesupport/lib/active_support/testing/assertions.rb | 9 ++------- activesupport/test/test_case_test.rb | 5 +++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index 46eed73d24..b24aa36ede 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -159,7 +159,7 @@ module ActiveSupport if to == UNTRACKED error = "#{expression.inspect} didn't change" error = "#{message}.\n#{error}" if message - assert_not_equal before, after, error + assert before != after, error else error = "#{expression.inspect} didn't change to #{to}" error = "#{message}.\n#{error}" if message @@ -190,12 +190,7 @@ module ActiveSupport error = "#{expression.inspect} did change to #{after}" error = "#{message}.\n#{error}" if message - - if before.nil? - assert_nil after, error - else - assert_equal before, after, error - end + assert before == after, error retval end diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index 9bc9183668..84e4953fe2 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -179,6 +179,7 @@ class AssertDifferenceTest < ActiveSupport::TestCase end def test_assert_changes_works_with_any_object + # Silences: instance variable @new_object not initialized. retval = silence_warnings do assert_changes :@new_object, from: nil, to: 42 do @new_object = 42 @@ -201,7 +202,7 @@ class AssertDifferenceTest < ActiveSupport::TestCase def test_assert_changes_with_to_and_case_operator token = nil - assert_changes -> { token }, to: /\w{32}/ do + assert_changes -> { token }, to: /\w{32}/ do token = SecureRandom.hex end end @@ -236,7 +237,7 @@ class AssertDifferenceTest < ActiveSupport::TestCase end end - assert_equal "@object.num should not change.\n\"@object.num\" did change to 1.\nExpected: 0\n Actual: 1", error.message + assert_equal "@object.num should not change.\n\"@object.num\" did change to 1", error.message end end -- cgit v1.2.3 From 5bbe6924529bc510d104b8bc3756a1e2f0a451dd Mon Sep 17 00:00:00 2001 From: Takumasa Ochi <4468155+aeroastro@users.noreply.github.com> Date: Tue, 7 Nov 2017 18:15:07 +0900 Subject: Fix typo on ActionDispatc::HTTP::FilterParameters --- actionpack/lib/action_dispatch/http/filter_parameters.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index b7141cc1b9..e50672e191 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -9,7 +9,7 @@ module ActionDispatch # sub-hashes of the params hash to filter. Filtering only certain sub-keys # from a hash is possible by using the dot notation: 'credit_card.number'. # If a block is given, each key and value of the params hash and all - # sub-hashes is passed to it, the value or key can be replaced using + # sub-hashes is passed to it, where the value or the key can be replaced using # String#replace or similar method. # # env["action_dispatch.parameter_filter"] = [:password] @@ -48,7 +48,7 @@ module ActionDispatch @filtered_env ||= env_filter.filter(@env) end - # Reconstructed a path with all sensitive GET parameters replaced. + # Reconstructs a path with all sensitive GET parameters replaced. def filtered_path @filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}" end -- cgit v1.2.3 From daf77db65d9d5e295ee3ba86605988875cb834e4 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Tue, 7 Nov 2017 09:06:23 -0500 Subject: Remove needless block parameter --- activestorage/lib/active_storage/service/s3_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb index 958b172efb..6957119780 100644 --- a/activestorage/lib/active_storage/service/s3_service.rb +++ b/activestorage/lib/active_storage/service/s3_service.rb @@ -85,7 +85,7 @@ module ActiveStorage end # Reads the object for the given key in chunks, yielding each to the block. - def stream(key, &block) + def stream(key) object = object_for(key) chunk_size = 5.megabytes -- cgit v1.2.3 From c57e914cd1517013ffdc1fd92f0f705444f26206 Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Tue, 7 Nov 2017 09:24:49 -0800 Subject: Performance improvements for add_method_to_attributes! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevents two string allocations per method call for common REST verbs plus a ~1.5x speedup for :get in particular ```ruby begin require "bundler/inline" rescue LoadError => e $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler" raise e end gemfile(true) do source "https://rubygems.org" gem "benchmark-ips" gem "rails" end def allocate_count GC.disable before = ObjectSpace.count_objects yield after = ObjectSpace.count_objects after.each { |k,v| after[k] = v - before[k] } after[:T_HASH] -= 1 # probe effect - we created the before hash. GC.enable result = after.reject { |k,v| v == 0 } GC.start result end @html_options = {} def master_version if @method && @method.to_s.downcase != "get" && @html_options["rel".freeze] !~ /nofollow/ @html_options["rel".freeze] = "#{@html_options["rel".freeze]} nofollow".lstrip end @html_options["data-method".freeze] = @method end def fast_version if method_not_get_method?(@method) && @html_options["rel".freeze] !~ /nofollow/ @html_options["rel".freeze] = "#{@html_options["rel".freeze]} nofollow".lstrip end @html_options["data-method".freeze] = @method end STRINGIFIED_COMMON_METHODS = { get: 'get', delete: 'delete', patch: 'patch', post: 'post', put: 'put', }.freeze def method_not_get_method?(method) return false unless method (STRINGIFIED_COMMON_METHODS[method] || method.to_s.downcase) != 'get' end puts 'get' @method = :get puts "master_version" puts allocate_count { 1000.times { master_version } } puts "fast_version" puts allocate_count { 1000.times { fast_version } } Benchmark.ips do |x| x.report("master_version") { master_version } x.report("fast_version") { fast_version } x.compare! end puts 'delete' @method = :delete puts "master_version" puts allocate_count { 1000.times { master_version } } puts "fast_version" puts allocate_count { 1000.times { fast_version } } Benchmark.ips do |x| x.report("master_version") { master_version } x.report("fast_version") { fast_version } x.compare! end ``` ``` get master_version {:FREE=>-1819, :T_STRING=>2052} fast_version {:FREE=>-1} Warming up -------------------------------------- master_version 140.839k i/100ms fast_version 175.639k i/100ms Calculating ------------------------------------- master_version 2.683M (± 7.1%) i/s - 13.380M in 5.013447s fast_version 3.988M (±10.1%) i/s - 19.847M in 5.039580s Comparison: fast_version: 3988340.3 i/s master_version: 2683336.2 i/s - 1.49x slower delete master_version {:FREE=>-5003, :T_STRING=>3003, :T_MATCH=>999, :T_IMEMO=>1000} fast_version {:FREE=>-3002, :T_STRING=>1001, :T_MATCH=>1000, :T_IMEMO=>1000} Warming up -------------------------------------- master_version 47.221k i/100ms fast_version 44.153k i/100ms Calculating ------------------------------------- master_version 597.881k (±11.4%) i/s - 2.975M in 5.047200s fast_version 686.014k (±11.6%) i/s - 3.400M in 5.036564s Comparison: fast_version: 686014.5 i/s master_version: 597881.4 i/s - same-ish: difference falls within error ``` --- actionview/lib/action_view/helpers/url_helper.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 9900e0cd03..02335c72ec 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -589,7 +589,7 @@ module ActionView end def add_method_to_attributes!(html_options, method) - if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/ + if method_not_get_method?(method) && html_options["rel"] !~ /nofollow/ if html_options["rel"].blank? html_options["rel"] = "nofollow" else @@ -599,6 +599,19 @@ module ActionView html_options["data-method"] = method end + STRINGIFIED_COMMON_METHODS = { + get: "get", + delete: "delete", + patch: "patch", + post: "post", + put: "put", + }.freeze + + def method_not_get_method?(method) + return false unless method + (STRINGIFIED_COMMON_METHODS[method] || method.to_s.downcase) != "get" + end + def token_tag(token = nil, form_options: {}) if token != false && protect_against_forgery? token ||= form_authenticity_token(form_options: form_options) -- cgit v1.2.3 From 67db41aa7f17c2d34eb5a914ac7a6b2574930ff4 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 8 Nov 2017 12:15:27 +0900 Subject: Do not run `active_storage:install` when bundle install is skipped In order to execute the `rails` command, need to run bundle install in advance. Therefore, if skipped bundle install, `rails` command may fail and should not do it. --- railties/lib/rails/generators/app_base.rb | 6 +++++- railties/test/generators/app_generator_test.rb | 16 +++++++++++++++- railties/test/generators/shared_generator_tests.rb | 1 - railties/test/railties/engine_test.rb | 4 ++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 59ca4c849f..60e54cc365 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -461,7 +461,11 @@ module Rails def run_active_storage unless skip_active_storage? - rails_command "active_storage:install", capture: options[:quiet] + if bundle_install? + rails_command "active_storage:install", capture: options[:quiet] + else + log("Active Storage installation was skipped. Please run 'bin/rails active_storage:install' to install Active Storage files.") + end end end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index c5a59edab2..1de79e82e2 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -68,7 +68,6 @@ DEFAULT_APP_FILES = %w( config/spring.rb config/storage.yml db - db/migrate db/seeds.rb lib lib/tasks @@ -304,6 +303,21 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "Gemfile", /^# gem 'mini_magick'/ end + def test_active_storage_install + command_check = -> command, _ do + @binstub_called ||= 0 + case command + when "active_storage:install" + @binstub_called += 1 + assert_equal 1, @binstub_called, "active_storage:install expected to be called once, but was called #{@install_called} times." + end + end + + generator.stub :rails_command, command_check do + quietly { generator.invoke_all } + end + end + def test_app_update_does_not_generate_active_storage_contents_when_skip_active_storage_is_given app_root = File.join(destination_root, "myapp") run_generator [app_root, "--skip-active-storage"] diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index ed09847443..ba8ee13526 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -222,7 +222,6 @@ module SharedGeneratorTests end assert_file "#{application_path}/config/storage.yml" - assert_directory "#{application_path}/db/migrate" assert_directory "#{application_path}/storage" assert_directory "#{application_path}/tmp/storage" diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 132b3b3a6e..fc710feb63 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -90,6 +90,8 @@ module RailtiesTest boot_rails Dir.chdir(app_path) do + # Install Active Storage migration file first so as not to affect test. + `bundle exec rake active_storage:install` output = `bundle exec rake bukkits:install:migrations` ["CreateUsers", "AddLastNameToUsers", "CreateSessions"].each do |migration_name| @@ -175,6 +177,8 @@ module RailtiesTest boot_rails Dir.chdir(app_path) do + # Install Active Storage migration file first so as not to affect test. + `bundle exec rake active_storage:install` output = `bundle exec rake railties:install:migrations`.split("\n") assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.first) -- cgit v1.2.3 From 8e1dca10cd98bdc8bb00592f3d90926309b22a18 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 8 Nov 2017 12:32:12 +0900 Subject: Remove unnecessary migration deletion Since isolation application is generated with the `--skip-gemfile` option, so `active_storage:install` is not executed. --- railties/test/application/bin_setup_test.rb | 4 ---- railties/test/application/rackup_test.rb | 1 - railties/test/application/rake/dbs_test.rb | 4 ---- railties/test/application/test_runner_test.rb | 5 ----- railties/test/application/test_test.rb | 5 ----- 5 files changed, 19 deletions(-) diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb index 9fac0435e4..54934dbe24 100644 --- a/railties/test/application/bin_setup_test.rb +++ b/railties/test/application/bin_setup_test.rb @@ -16,8 +16,6 @@ module ApplicationTests def test_bin_setup Dir.chdir(app_path) do - FileUtils.rm_rf("db/migrate") - app_file "db/schema.rb", <<-RUBY ActiveRecord::Schema.define(version: 20140423102712) do create_table(:articles) {} @@ -39,8 +37,6 @@ module ApplicationTests def test_bin_setup_output Dir.chdir(app_path) do - FileUtils.rm_rf("db/migrate") - app_file "db/schema.rb", "" output = `bin/setup 2>&1` diff --git a/railties/test/application/rackup_test.rb b/railties/test/application/rackup_test.rb index 1dcc2826f0..383f18a7da 100644 --- a/railties/test/application/rackup_test.rb +++ b/railties/test/application/rackup_test.rb @@ -26,7 +26,6 @@ module ApplicationTests test "config.ru can be racked up" do Dir.chdir app_path do - FileUtils.rm_rf("db/migrate") @app = rackup assert_welcome get("/") end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 009f2887c9..fd22477539 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -287,8 +287,6 @@ module ApplicationTests ENV.delete "RAILS_ENV" ENV.delete "RACK_ENV" - Dir.chdir(app_path) { FileUtils.rm_rf("db/migrate") } - app_file "db/schema.rb", <<-RUBY ActiveRecord::Schema.define(version: "1") do create_table :users do |t| @@ -310,8 +308,6 @@ module ApplicationTests end test "db:setup sets ar_internal_metadata" do - Dir.chdir(app_path) { FileUtils.rm_rf("db/migrate") } - app_file "db/schema.rb", "" rails "db:setup" diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index 30bd283b48..e92a0466dd 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -11,7 +11,6 @@ module ApplicationTests def setup build_app create_schema - remove_migrations end def teardown @@ -728,10 +727,6 @@ module ApplicationTests app_file "db/schema.rb", "" end - def remove_migrations - Dir.chdir(app_path) { FileUtils.rm_rf("db/migrate") } - end - def create_test_file(path = :unit, name = "test", pass: true) app_file "test/#{path}/#{name}_test.rb", <<-RUBY require 'test_helper' diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb index 50238ed682..0a51e98656 100644 --- a/railties/test/application/test_test.rb +++ b/railties/test/application/test_test.rb @@ -8,7 +8,6 @@ module ApplicationTests def setup build_app - remove_migrations end def teardown @@ -321,10 +320,6 @@ Expected: ["id", "name"] end private - def remove_migrations - Dir.chdir(app_path) { FileUtils.rm_rf("db/migrate") } - end - def assert_unsuccessful_run(name, message) result = run_test_file(name) assert_not_equal 0, $?.to_i -- cgit v1.2.3 From 8d60dcd024e72be7aa90de62fe8e1005c2e254c2 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 8 Nov 2017 16:46:57 +0900 Subject: Fix output of `select_tag` with `include_blank: true` [ci skip] Since #24923, if use `select_tag` with `include_blank: true`, an empty label is added. --- actionview/lib/action_view/helpers/form_tag_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 80d53c2c6e..e86e18dd78 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -115,7 +115,7 @@ module ActionView # # # # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true - # # => + # # => # # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: "All" # # => -- cgit v1.2.3 From cfe46db8e29bbd0b14e4111ea8e37ea74be058d5 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 8 Nov 2017 17:08:23 +0900 Subject: Use released `redis-namespace` instead of master version The `redis-namespace` 1.6.0 includes redis-rb 4.0 support. --- Gemfile | 3 +-- Gemfile.lock | 11 +++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 2704e1e806..3f8cd57853 100644 --- a/Gemfile +++ b/Gemfile @@ -80,8 +80,7 @@ group :cable do gem "hiredis", require: false gem "redis", "~> 4.0", require: false - # For Redis 4.0 support. Unreleased 9cb81bf. - gem "redis-namespace", github: "resque/redis-namespace" + gem "redis-namespace" gem "websocket-client-simple", github: "matthewd/websocket-client-simple", branch: "close-race", require: false diff --git a/Gemfile.lock b/Gemfile.lock index cc3b0508c5..d0bfe589be 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -52,13 +52,6 @@ GIT sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) -GIT - remote: https://github.com/resque/redis-namespace.git - revision: 1403f511f6ae1ec9a8f330298a4cacf73fb10afc - specs: - redis-namespace (1.5.3) - redis (>= 3.0.4) - GIT remote: https://github.com/robin850/sdoc.git revision: 0e340352f3ab2f196c8a8743f83c2ee286e4f71c @@ -409,6 +402,8 @@ GEM rdoc (5.1.0) redcarpet (3.2.3) redis (4.0.1) + redis-namespace (1.6.0) + redis (>= 3.0.4) representable (3.0.4) declarative (< 0.1.0) declarative-option (< 0.2.0) @@ -563,7 +558,7 @@ DEPENDENCIES rb-inotify! redcarpet (~> 3.2.3) redis (~> 4.0) - redis-namespace! + redis-namespace resque resque-scheduler! rubocop (>= 0.47) -- cgit v1.2.3 From 2b434d6f79813dcad162b158fd2b60e34a725ba1 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Tue, 7 Nov 2017 11:33:08 +0000 Subject: Allow `Range#include?` on TWZ ranges In #11474 we prevented TWZ ranges being iterated over which matched Ruby's handling of Time ranges and as a consequence `include?` stopped working with both Time ranges and TWZ ranges. However in ruby/ruby@b061634 support was added for `include?` to use `cover?` for 'linear' objects. Since we have no way of making Ruby consider TWZ instances as 'linear' we have to override `Range#include?`. Fixes #30799. --- activesupport/CHANGELOG.md | 13 ++++++++++++ activesupport/lib/active_support/core_ext/range.rb | 1 + .../lib/active_support/core_ext/range/each.rb | 4 +++- .../core_ext/range/include_time_with_zone.rb | 23 ++++++++++++++++++++++ activesupport/test/core_ext/range_ext_test.rb | 9 ++++++--- 5 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/range/include_time_with_zone.rb diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 9c27c68919..780c887b49 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,16 @@ +* Allow `Range#include?` on TWZ ranges + + In #11474 we prevented TWZ ranges being iterated over which matched + Ruby's handling of Time ranges and as a consequence `include?` + stopped working with both Time ranges and TWZ ranges. However in + ruby/ruby@b061634 support was added for `include?` to use `cover?` + for 'linear' objects. Since we have no way of making Ruby consider + TWZ instances as 'linear' we have to override `Range#include?`. + + Fixes #30799. + + *Andrew White* + * Fix acronym support in `humanize` Acronym inflections are stored with lowercase keys in the hash but diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb index 51ae0ddd21..4074e91d17 100644 --- a/activesupport/lib/active_support/core_ext/range.rb +++ b/activesupport/lib/active_support/core_ext/range.rb @@ -2,5 +2,6 @@ require "active_support/core_ext/range/conversions" require "active_support/core_ext/range/include_range" +require "active_support/core_ext/range/include_time_with_zone" require "active_support/core_ext/range/overlaps" require "active_support/core_ext/range/each" diff --git a/activesupport/lib/active_support/core_ext/range/each.rb b/activesupport/lib/active_support/core_ext/range/each.rb index cdff6393d7..2f22cd0e92 100644 --- a/activesupport/lib/active_support/core_ext/range/each.rb +++ b/activesupport/lib/active_support/core_ext/range/each.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "active_support/time_with_zone" + module ActiveSupport module EachTimeWithZone #:nodoc: def each(&block) @@ -15,7 +17,7 @@ module ActiveSupport private def ensure_iteration_allowed - raise TypeError, "can't iterate from #{first.class}" if first.is_a?(Time) + raise TypeError, "can't iterate from #{first.class}" if first.is_a?(TimeWithZone) end end end diff --git a/activesupport/lib/active_support/core_ext/range/include_time_with_zone.rb b/activesupport/lib/active_support/core_ext/range/include_time_with_zone.rb new file mode 100644 index 0000000000..5f80acf68e --- /dev/null +++ b/activesupport/lib/active_support/core_ext/range/include_time_with_zone.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "active_support/time_with_zone" + +module ActiveSupport + module IncludeTimeWithZone #:nodoc: + # Extends the default Range#include? to support ActiveSupport::TimeWithZone. + # + # (1.hour.ago..1.hour.from_now).include?(Time.current) # => true + # + def include?(value) + if first.is_a?(TimeWithZone) + cover?(value) + elsif last.is_a?(TimeWithZone) + cover?(value) + else + super + end + end + end +end + +Range.prepend(ActiveSupport::IncludeTimeWithZone) diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index 0467123e55..049fac8fd4 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -121,9 +121,12 @@ class RangeTest < ActiveSupport::TestCase def test_include_on_time_with_zone twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Eastern Time (US & Canada)"] , Time.utc(2006, 11, 28, 10, 30)) - assert_raises TypeError do - ((twz - 1.hour)..twz).include?(twz) - end + assert ((twz - 1.hour)..twz).include?(twz) + end + + def test_case_equals_on_time_with_zone + twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Eastern Time (US & Canada)"] , Time.utc(2006, 11, 28, 10, 30)) + assert ((twz - 1.hour)..twz) === twz end def test_date_time_with_each -- cgit v1.2.3 From 0c2cb880e34c943275758ca6a6ff84afa7a29fba Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 8 Nov 2017 20:08:19 +0900 Subject: Don't expose internal methods in `Preloader::ThroughAssociation` `through_reflection` and `source_reflection` are used only in the class. --- .../associations/preloader/through_association.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index b1813ba66b..762275fbad 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -4,14 +4,6 @@ module ActiveRecord module Associations class Preloader module ThroughAssociation #:nodoc: - def through_reflection - reflection.through_reflection - end - - def source_reflection - reflection.source_reflection - end - def run(preloader) already_loaded = owners.first.association(through_reflection.name).loaded? through_scope = through_scope() @@ -48,6 +40,13 @@ module ActiveRecord end private + def through_reflection + reflection.through_reflection + end + + def source_reflection + reflection.source_reflection + end def preload_index @preload_index ||= @preloaded_records.each_with_object({}).with_index do |(id, result), index| -- cgit v1.2.3 From 65aa0b7e3448f849a91b6f510b78d4303ff44dbc Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 8 Nov 2017 20:28:52 +0900 Subject: Don't expose accessors which are internal used only --- activerecord/lib/active_record/associations/preloader.rb | 5 +++-- activerecord/lib/active_record/associations/preloader/association.rb | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index e1754d4a19..5a93a89d0a 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -166,8 +166,6 @@ module ActiveRecord end class AlreadyLoaded # :nodoc: - attr_reader :owners, :reflection - def initialize(klass, owners, reflection, preload_scope) @owners = owners @reflection = reflection @@ -178,6 +176,9 @@ module ActiveRecord def preloaded_records owners.flat_map { |owner| owner.association(reflection.name).target } end + + protected + attr_reader :owners, :reflection end # Returns a class containing the logic needed to load preload the data diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index e77761692d..19c337dc39 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -4,7 +4,6 @@ module ActiveRecord module Associations class Preloader class Association #:nodoc: - attr_reader :owners, :reflection, :preload_scope, :model, :klass attr_reader :preloaded_records def initialize(klass, owners, reflection, preload_scope) @@ -28,6 +27,9 @@ module ActiveRecord end end + protected + attr_reader :owners, :reflection, :preload_scope, :model, :klass + private # The name of the key on the associated records def association_key_name -- cgit v1.2.3 From ae6361b88c58c5dc1e86833e0299d031bb573fe1 Mon Sep 17 00:00:00 2001 From: Bogdan Gusiev Date: Tue, 7 Nov 2017 17:23:56 +0200 Subject: Set counter caches to correct values in fixtures --- .../cases/associations/cascaded_eager_loading_test.rb | 2 +- .../cases/associations/has_many_associations_test.rb | 2 +- activerecord/test/cases/relations_test.rb | 18 +++++++++--------- activerecord/test/fixtures/other_posts.yml | 1 + activerecord/test/fixtures/posts.yml | 8 ++++++++ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index e69cfe5e52..829e12fbc8 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -37,7 +37,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_with_hmt_does_not_table_name_collide_when_joining_associations authors = Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).to_a - assert_equal 1, assert_no_queries { authors.size } + assert_equal 3, assert_no_queries { authors.size } assert_equal 10, assert_no_queries { authors[0].comments.size } end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 6bd11a5d81..4ca11af791 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -831,7 +831,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_find_scoped_grouped_having - assert_equal 1, authors(:david).popular_grouped_posts.length + assert_equal 2, authors(:david).popular_grouped_posts.length assert_equal 0, authors(:mary).popular_grouped_posts.length end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 72433d1e8e..844be0d0bf 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -927,13 +927,13 @@ class RelationTest < ActiveRecord::TestCase assert_equal 11, posts.count(:all) assert_equal 11, posts.count(:id) - assert_equal 1, posts.where("comments_count > 1").count - assert_equal 9, posts.where(comments_count: 0).count + assert_equal 3, posts.where("comments_count > 1").count + assert_equal 6, posts.where(comments_count: 0).count end def test_count_with_block posts = Post.all - assert_equal 10, posts.count { |p| p.comments_count.even? } + assert_equal 8, posts.count { |p| p.comments_count.even? } end def test_count_on_association_relation @@ -950,10 +950,10 @@ class RelationTest < ActiveRecord::TestCase def test_count_with_distinct posts = Post.all - assert_equal 3, posts.distinct(true).count(:comments_count) + assert_equal 4, posts.distinct(true).count(:comments_count) assert_equal 11, posts.distinct(false).count(:comments_count) - assert_equal 3, posts.distinct(true).select(:comments_count).count + assert_equal 4, posts.distinct(true).select(:comments_count).count assert_equal 11, posts.distinct(false).select(:comments_count).count end @@ -992,7 +992,7 @@ class RelationTest < ActiveRecord::TestCase best_posts = posts.where(comments_count: 0) best_posts.load # force load - assert_no_queries { assert_equal 9, best_posts.size } + assert_no_queries { assert_equal 6, best_posts.size } end def test_size_with_limit @@ -1003,7 +1003,7 @@ class RelationTest < ActiveRecord::TestCase best_posts = posts.where(comments_count: 0) best_posts.load # force load - assert_no_queries { assert_equal 9, best_posts.size } + assert_no_queries { assert_equal 6, best_posts.size } end def test_size_with_zero_limit @@ -1026,7 +1026,7 @@ class RelationTest < ActiveRecord::TestCase def test_count_complex_chained_relations posts = Post.select("comments_count").where("id is not null").group("author_id").where("comments_count > 0") - expected = { 1 => 2 } + expected = { 1 => 4, 2 => 1 } assert_equal expected, posts.count end @@ -1781,7 +1781,7 @@ class RelationTest < ActiveRecord::TestCase end test "arel_attribute respects a custom table" do - assert_equal [posts(:welcome)], custom_post_relation.ranked_by_comments.limit_by(1).to_a + assert_equal [posts(:sti_comments)], custom_post_relation.ranked_by_comments.limit_by(1).to_a end test "alias_tracker respects a custom table" do diff --git a/activerecord/test/fixtures/other_posts.yml b/activerecord/test/fixtures/other_posts.yml index 39ff763547..3e11a33802 100644 --- a/activerecord/test/fixtures/other_posts.yml +++ b/activerecord/test/fixtures/other_posts.yml @@ -5,3 +5,4 @@ second_welcome: author_id: 1 title: Welcome to the another weblog body: It's really nice today + comments_count: 1 diff --git a/activerecord/test/fixtures/posts.yml b/activerecord/test/fixtures/posts.yml index 86d46f753a..8d7e1e0ae7 100644 --- a/activerecord/test/fixtures/posts.yml +++ b/activerecord/test/fixtures/posts.yml @@ -28,6 +28,7 @@ sti_comments: author_id: 1 title: sti comments body: hello + comments_count: 5 type: Post sti_post_and_comments: @@ -35,6 +36,7 @@ sti_post_and_comments: author_id: 1 title: sti me body: hello + comments_count: 2 type: StiPost sti_habtm: @@ -50,6 +52,8 @@ eager_other: title: eager loading with OR'd conditions body: hello type: Post + comments_count: 1 + tags_count: 3 misc_by_bob: id: 8 @@ -57,6 +61,7 @@ misc_by_bob: title: misc post by bob body: hello type: Post + tags_count: 1 misc_by_mary: id: 9 @@ -64,6 +69,7 @@ misc_by_mary: title: misc post by mary body: hello type: Post + tags_count: 1 other_by_bob: id: 10 @@ -71,6 +77,7 @@ other_by_bob: title: other post by bob body: hello type: Post + tags_count: 1 other_by_mary: id: 11 @@ -78,3 +85,4 @@ other_by_mary: title: other post by mary body: hello type: Post + tags_count: 1 -- cgit v1.2.3 From af91eff3fa395a0a57f8002b3b92e5d053cd2d7d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 9 Nov 2017 05:08:26 +0900 Subject: Consolidate redundant `if` and `unless` with the same condition --- activerecord/test/cases/transaction_isolation_test.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/activerecord/test/cases/transaction_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb index b1ebccdcc3..eaafd13360 100644 --- a/activerecord/test/cases/transaction_isolation_test.rb +++ b/activerecord/test/cases/transaction_isolation_test.rb @@ -15,9 +15,7 @@ unless ActiveRecord::Base.connection.supports_transaction_isolation? end end end -end - -if ActiveRecord::Base.connection.supports_transaction_isolation? +else class TransactionIsolationTest < ActiveRecord::TestCase self.use_transactional_tests = false -- cgit v1.2.3 From 89603b41edbaaed5cab4964a30caa8bda9285879 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 9 Nov 2017 05:27:30 +0900 Subject: `Mysql2Adapter` should pass `ConcurrentTransactionTest` --- activerecord/test/cases/transactions_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 7fd125ab74..91032945a6 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -954,7 +954,7 @@ class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase end end if Topic.connection.supports_savepoints? -if current_adapter?(:PostgreSQLAdapter) +if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter) class ConcurrentTransactionTest < TransactionTest # This will cause transactions to overlap and fail unless they are performed on # separate database connections. -- cgit v1.2.3 From d5ad63766eb9e31e3589a3571142042ab1a15bb4 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 8 Nov 2017 21:39:35 +0000 Subject: Remove redundant passing --skip-active-storage in test cases These were added in #30101, after #31084 it became redundant. --- railties/test/generators/app_generator_test.rb | 4 ++-- railties/test/generators/plugin_generator_test.rb | 2 +- railties/test/generators/plugin_test_runner_test.rb | 2 +- railties/test/generators/shared_generator_tests.rb | 2 +- railties/test/generators/test_runner_in_engine_test.rb | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 1de79e82e2..78962ee30b 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -423,7 +423,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end def test_config_jdbcmysql_database - run_generator([destination_root, "-d", "jdbcmysql", "--skip-active-storage"]) + run_generator([destination_root, "-d", "jdbcmysql"]) assert_file "config/database.yml", /mysql/ assert_gem "activerecord-jdbcmysql-adapter" end @@ -441,7 +441,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end def test_config_jdbc_database - run_generator([destination_root, "-d", "jdbc", "--skip-active-storage"]) + run_generator([destination_root, "-d", "jdbc"]) assert_file "config/database.yml", /jdbc/ assert_file "config/database.yml", /mssql/ assert_gem "activerecord-jdbc-adapter" diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 06d223e26d..49256883d6 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -230,7 +230,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase end def test_ensure_that_tests_work - run_generator [destination_root, "--skip-active-storage"] + run_generator FileUtils.cd destination_root quietly { system "bundle install" } assert_match(/1 runs, 1 assertions, 0 failures, 0 errors/, `bin/test 2>&1`) diff --git a/railties/test/generators/plugin_test_runner_test.rb b/railties/test/generators/plugin_test_runner_test.rb index 48a3bcadcd..89c3f1e496 100644 --- a/railties/test/generators/plugin_test_runner_test.rb +++ b/railties/test/generators/plugin_test_runner_test.rb @@ -7,7 +7,7 @@ class PluginTestRunnerTest < ActiveSupport::TestCase def setup @destination_root = Dir.mktmpdir("bukkits") - Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --skip-bundle --skip-active-storage` } + Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --skip-bundle` } plugin_file "test/dummy/db/schema.rb", "" end diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index ba8ee13526..6b746f3fed 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -63,7 +63,7 @@ module SharedGeneratorTests end def test_shebang_is_added_to_rails_file - run_generator [destination_root, "--ruby", "foo/bar/baz", "--full", "--skip-active-storage"] + run_generator [destination_root, "--ruby", "foo/bar/baz", "--full"] assert_file "bin/rails", /#!foo\/bar\/baz/ end diff --git a/railties/test/generators/test_runner_in_engine_test.rb b/railties/test/generators/test_runner_in_engine_test.rb index 2acd96ecd4..0e15b5e388 100644 --- a/railties/test/generators/test_runner_in_engine_test.rb +++ b/railties/test/generators/test_runner_in_engine_test.rb @@ -7,7 +7,7 @@ class TestRunnerInEngineTest < ActiveSupport::TestCase def setup @destination_root = Dir.mktmpdir("bukkits") - Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --full --skip-bundle --skip-active-storage` } + Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --full --skip-bundle` } plugin_file "test/dummy/db/schema.rb", "" end -- cgit v1.2.3 From 6f123341d9e8807bb83c6a8214ec54d3b52c82a3 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Wed, 8 Nov 2017 22:07:12 +0000 Subject: Change output log about skipping instalation of Active Storage Using of "`" is preferable over "'" to express console command in output log --- railties/lib/rails/generators/app_base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 60e54cc365..bdeddff645 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -464,7 +464,7 @@ module Rails if bundle_install? rails_command "active_storage:install", capture: options[:quiet] else - log("Active Storage installation was skipped. Please run 'bin/rails active_storage:install' to install Active Storage files.") + log("Active Storage installation was skipped. Please run `bin/rails active_storage:install` to install Active Storage files.") end end end -- cgit v1.2.3 From 801fbde87deae5c01c581a6e4ef1157d27185b32 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 8 Nov 2017 21:22:55 +0000 Subject: Run `ConcurrentTransactionTest` if `supports_transaction_isolation?` returns true Not only postgresql or mysql2 adapter, Oracle enhanced adapter whose default isolation level is read commited, passes these two test cases. `ConcurrentTransactionTest#test_transaction_per_thread` `ConcurrentTransactionTest#test_transaction_isolation__read_committed` ```ruby $ ARCONN=oracle bin/test test/cases/transactions_test.rb:961 -v Using oracle Run options: -v --seed 18865 ConcurrentTransactionTest#test_transaction_per_thread = 0.98 s = . Finished in 1.061036s, 0.9425 runs/s, 5.6549 assertions/s. 1 runs, 6 assertions, 0 failures, 0 errors, 0 skips ``` ```ruby $ ARCONN=oracle bin/test test/cases/transactions_test.rb:979 -v Using oracle Run options: -v --seed 13341 ConcurrentTransactionTest#test_transaction_isolation__read_committed = 1.85 s = . Finished in 1.928637s, 0.5185 runs/s, 10.3700 assertions/s. 1 runs, 20 assertions, 0 failures, 0 errors, 0 skips $ ``` Also, regardless it is a file based or memory based these tests could fail with SQLite3Adapter. (Extra CR added to make lines shorter) ```ruby $ ARCONN=sqlite3 bin/test test/cases/transactions_test.rb:961 -v Using sqlite3 Run options: -v --seed 18815 ConcurrentTransactionTest#test_transaction_per_thread = /home/yahonda/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/sqlite3-1.3.13/lib/sqlite3/statement.rb:108:in `step': SQLite3::BusyException: database is locked: UPDATE "topics" SET "approved" = ?, "updated_at" = ? WHERE "topics"."id" = ? (ActiveRecord::StatementInvalid) ``` ```ruby $ ARCONN=sqlite3 bin/test test/cases/transactions_test.rb:979 -v Using sqlite3 Run options: -v --seed 25520 ConcurrentTransactionTest#test_transaction_isolation__read_committed = 0.12 s = E /home/yahonda/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/sqlite3-1.3.13/lib/sqlite3/statement.rb:108:in `step': SQLite3::BusyException: database is locked: UPDATE "developers" SET "salary" = ?, "updated_at" = ?, "updated_on" = ? WHERE "developers"."id" = ? (ActiveRecord::StatementInvalid) ``` --- activerecord/test/cases/transactions_test.rb | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 91032945a6..5c8ae4d3cb 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -954,27 +954,25 @@ class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase end end if Topic.connection.supports_savepoints? -if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter) +if ActiveRecord::Base.connection.supports_transaction_isolation? class ConcurrentTransactionTest < TransactionTest # This will cause transactions to overlap and fail unless they are performed on # separate database connections. - unless in_memory_db? - def test_transaction_per_thread - threads = 3.times.map do - Thread.new do - Topic.transaction do - topic = Topic.find(1) - topic.approved = !topic.approved? - assert topic.save! - topic.approved = !topic.approved? - assert topic.save! - end - Topic.connection.close + def test_transaction_per_thread + threads = 3.times.map do + Thread.new do + Topic.transaction do + topic = Topic.find(1) + topic.approved = !topic.approved? + assert topic.save! + topic.approved = !topic.approved? + assert topic.save! end + Topic.connection.close end - - threads.each(&:join) end + + threads.each(&:join) end # Test for dirty reads among simultaneous transactions. -- cgit v1.2.3 From 2a6852c6ef272b404f97058b0afc9d460e05777c Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 9 Nov 2017 11:28:39 +0900 Subject: Correctly kill the server started with ujs test `Kernel.#spawn` execute command via the shell if contains shell metacharacters in the command. In that case, return value of `spawn` is pid of the shell, not the server. Therefore, just killing the pid will leave the process of server. In order to correctly kill the server, send a signal to the process group, not the process. --- actionview/Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionview/Rakefile b/actionview/Rakefile index 9e5ef35334..8650f541b0 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -32,7 +32,7 @@ namespace :test do task :ujs do begin Dir.mkdir("log") - pid = spawn("bundle exec rackup test/ujs/config.ru -p 4567 -s puma > log/test.log 2>&1") + pid = spawn("bundle exec rackup test/ujs/config.ru -p 4567 -s puma > log/test.log 2>&1", pgroup: true) start_time = Time.now @@ -48,7 +48,7 @@ namespace :test do system("npm run lint && bundle exec ruby ../ci/qunit-selenium-runner.rb http://localhost:4567/") status = $?.exitstatus ensure - Process.kill("KILL", pid) if pid + Process.kill("KILL", -pid) if pid FileUtils.rm_rf("log") end -- cgit v1.2.3 From be6e1b8f7dbce1940f47339657faab2c1fdeaa54 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 9 Nov 2017 16:53:55 +0900 Subject: Should pass `test_no_locks_no_wait` not only on `PostgreSQLAdapter` and `OracleAdapter` --- activerecord/test/cases/locking_test.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 6791d50940..e857180bd1 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -611,14 +611,12 @@ unless in_memory_db? end end - if current_adapter?(:PostgreSQLAdapter, :OracleAdapter) - def test_no_locks_no_wait - first, second = duel { Person.find 1 } - assert first.end > second.end - end - - private + def test_no_locks_no_wait + first, second = duel { Person.find 1 } + assert first.end > second.end + end + private def duel(zzz = 5) t0, t1, t2, t3 = nil, nil, nil, nil @@ -646,6 +644,5 @@ unless in_memory_db? assert t3 > t2 [t0.to_f..t1.to_f, t2.to_f..t3.to_f] end - end end end -- cgit v1.2.3 From 4022f33416a00fd6a18989aa54f60573f48be0a0 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 9 Nov 2017 17:12:07 +0900 Subject: Use `Tempfile.create` Instead of `Dir::Tmpname.make_tmpname`, an internal method which does not guarantee uniqueness, use `Tempfile.create`. --- actionpack/test/controller/log_subscriber_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index f0f106c8ba..4c59bea193 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -98,7 +98,7 @@ class ACLogSubscriberTest < ActionController::TestCase @old_logger = ActionController::Base.logger - @cache_path = File.join Dir.tmpdir, Dir::Tmpname.make_tmpname("tmp", "cache") + @cache_path = Tempfile.create(%w"tmp cache", Dir.tmpdir) @controller.cache_store = :file_store, @cache_path ActionController::LogSubscriber.attach_to :action_controller end -- cgit v1.2.3 From d1e0bc7c17b1be2766e9fca228b4c61e01988b34 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 9 Nov 2017 20:54:24 +0900 Subject: Do not show credentials in generators help Since credentials generator is executed via the credentials command and does not need to be executed directly, so it is not necessary to show it in help. --- railties/lib/rails/generators.rb | 1 + railties/test/application/generators_test.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 2d265818f7..5592e8d78e 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -218,6 +218,7 @@ module Rails rails.delete("app") rails.delete("plugin") rails.delete("encrypted_secrets") + rails.delete("credentials") hidden_namespaces.each { |n| groups.delete(n.to_s) } diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index 47c815d221..e5e557d204 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -188,10 +188,11 @@ module ApplicationTests Rails::Command.send(:remove_const, "APP_PATH") end - test "help does not show hidden namespaces" do + test "help does not show hidden namespaces and hidden commands" do FileUtils.cd(rails_root) do output = rails("generate", "--help") assert_no_match "active_record:migration", output + assert_no_match "credentials", output output = rails("destroy", "--help") assert_no_match "active_record:migration", output -- cgit v1.2.3 From f989b341eccc6a86fd1ddfff7f1441920855c84e Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Wed, 8 Feb 2017 11:23:26 -0700 Subject: add config to check arguments to unsafe AR methods --- .../lib/active_record/attribute_methods.rb | 16 ++ activerecord/lib/active_record/core.rb | 9 ++ activerecord/lib/active_record/querying.rb | 21 ++- .../lib/active_record/relation/calculations.rb | 42 +++-- .../lib/active_record/relation/query_methods.rb | 49 ++++++ activerecord/test/cases/unsafe_raw_sql_test.rb | 177 +++++++++++++++++++++ 6 files changed, 293 insertions(+), 21 deletions(-) create mode 100644 activerecord/test/cases/unsafe_raw_sql_test.rb diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 23d2aef214..fa0d79ba5f 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -167,6 +167,22 @@ module ActiveRecord end end + # Can the given name be treated as a column name? Returns true if name + # is attribute or attribute alias. + # + # class Person < ActiveRecord::Base + # end + # + # Person.respond_to_attribute?(:name) + # # => true + # + # Person.respond_to_attribute?("foo") + # # => false + def respond_to_attribute?(name) + name = name.to_s + attribute_names.include?(name) || attribute_aliases.include?(name) + end + # Returns true if the given attribute exists, otherwise false. # # class Person < ActiveRecord::Base diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 0f7a503c90..b1e3f71dfe 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -76,6 +76,15 @@ module ActiveRecord # scope being ignored is error-worthy, rather than a warning. mattr_accessor :error_on_ignored_order, instance_writer: false, default: false + # :singleton-method: + # Specify the behavior for unsafe raw query methods. Values are as follows + # enabled - Unsafe raw SQL can be passed to query methods. + # deprecated - Warnings are logged when unsafe raw SQL is passed to + # query methods. + # disabled - Unsafe raw SQL passed to query methods results in + # ArguementError. + mattr_accessor :allow_unsafe_raw_sql, instance_writer: false, default: :enabled + ## # :singleton-method: # Specify whether or not to use timestamps for migration versions diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index 3996d5661f..0233835bcf 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -2,18 +2,25 @@ module ActiveRecord module Querying - delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :none?, :one?, to: :all - delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!, to: :all + delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, + :any?, :many?, :none?, :one?, to: :all + delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, + :fifth!, :forty_two, :forty_two!, :third_to_last, :third_to_last!, + :second_to_last, :second_to_last!, to: :all delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all - delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all + delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, + to: :all delegate :find_by, :find_by!, to: :all delegate :destroy_all, :delete_all, :update_all, to: :all delegate :find_each, :find_in_batches, :in_batches, to: :all - delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or, - :where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly, :extending, - :having, :create_with, :distinct, :references, :none, :unscope, :merge, to: :all + delegate :select, :group, :order, :unsafe_raw_order, :except, :reorder, + :unsafe_raw_reorder, :limit, :offset, :joins, :left_joins, + :left_outer_joins, :or, :where, :rewhere, :preload, :eager_load, + :includes, :from, :lock, :readonly, :extending, :having, + :create_with, :distinct, :references, :none, :unscope, :merge, + to: :all delegate :count, :average, :minimum, :maximum, :sum, :calculate, to: :all - delegate :pluck, :ids, to: :all + delegate :pluck, :unsafe_raw_pluck, :ids, to: :all # Executes a custom SQL query against your database and returns all the results. The results will # be returned as an array with columns requested encapsulated as attributes of the model you call diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 11256ab3d9..dea7542f03 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -175,21 +175,13 @@ module ActiveRecord # See also #ids. # def pluck(*column_names) - if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty? - return records.pluck(*column_names) - end + _pluck(column_names, @klass.allow_unsafe_raw_sql == :enabled) + end - if has_include?(column_names.first) - relation = apply_join_dependency - relation.pluck(*column_names) - else - relation = spawn - relation.select_values = column_names.map { |cn| - @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn - } - result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) } - result.cast_values(klass.attribute_types) - end + # Same as #pluck but allows raw SQL regardless of `allow_unsafe_raw_sql` + # config setting. + def unsafe_raw_pluck(*column_names) + _pluck(column_names, true) end # Pluck all the ID's for the relation using the table's primary key @@ -202,6 +194,28 @@ module ActiveRecord private + def _pluck(column_names, unsafe_raw) + unrecognized = column_names.reject do |cn| + @klass.respond_to_attribute?(cn) + end + + if loaded? && unrecognized.none? + records.pluck(*column_names) + elsif has_include?(column_names.first) + relation = apply_join_dependency + relation.pluck(*column_names) + elsif unsafe_raw || unrecognized.none? + relation = spawn + relation.select_values = column_names.map { |cn| + @klass.respond_to_attribute?(cn) ? arel_attribute(cn) : cn + } + result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) } + result.cast_values(klass.attribute_types) + else + raise ArgumentError, "Invalid column name: #{unrecognized}" + end + end + def has_include?(column_name) eager_loading? || (includes_values.present? && column_name && column_name != :all) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 897ff5c8af..63b1d8e154 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -295,7 +295,22 @@ module ActiveRecord spawn.order!(*args) end + # Same as #order but allows raw SQL regardless of `allow_unsafe_raw_sql` + # config setting. + def unsafe_raw_order(*args) # :nodoc: + check_if_method_has_arguments!(:order, args) + spawn.unsafe_raw_order!(*args) + end + + # Same as #order but operates on relation in-place instead of copying. def order!(*args) # :nodoc: + restrict_order_args(args) unless klass.allow_unsafe_raw_sql == :enabled + unsafe_raw_order!(*args) + end + + # Same as #order! but allows raw SQL regardless of `allow_unsafe_raw_sql` + # config setting. + def unsafe_raw_order!(*args) # :nodoc: preprocess_order_args(args) self.order_values += args @@ -316,7 +331,22 @@ module ActiveRecord spawn.reorder!(*args) end + # Same as #reorder but allows raw SQL regardless of `allow_unsafe_raw_sql` + # config setting. + def unsafe_raw_reorder(*args) # :nodoc: + check_if_method_has_arguments!(:reorder, args) + spawn.unsafe_raw_reorder!(*args) + end + + # Same as #reorder but operates on relation in-place instead of copying. def reorder!(*args) # :nodoc: + restrict_order_args(args) unless klass.allow_unsafe_raw_sql == :enabled + unsafe_raw_reorder! + end + + # Same as #reorder! but allows raw SQL regardless of `allow_unsafe_raw_sql` + # config setting. + def unsafe_raw_reorder!(*args) # :nodoc: preprocess_order_args(args) self.reordering_value = true @@ -1139,6 +1169,25 @@ module ActiveRecord end.flatten! end + # Only allow column names and directions as arguments to #order and + # #reorder. Other arguments will cause an ArugmentError to be raised. + def restrict_order_args(args) + args = args.dup + orderings = args.extract_options! + columns = args | orderings.keys + + unrecognized = columns.reject { |c| klass.respond_to_attribute?(c) } + if unrecognized.any? + raise ArgumentError, "Invalid order column: #{unrecognized}" + end + + # TODO: find a better list of modifiers. + unrecognized = orderings.values.reject { |d| VALID_DIRECTIONS.include?(d.to_s) } + if unrecognized.any? + raise ArgumentError, "Invalid order direction: #{unrecognized}" + end + end + # Checks to make sure that the arguments are not blank. Note that if some # blank-like object were initially passed into the query method, then this # method will not raise an error. diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb new file mode 100644 index 0000000000..d05f0f12d9 --- /dev/null +++ b/activerecord/test/cases/unsafe_raw_sql_test.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +require "cases/helper" +require "models/post" +require "models/comment" + +class UnsafeRawSqlTest < ActiveRecord::TestCase + fixtures :posts, :comments + + test "order: allows string column name" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.order("title").pluck(:id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_order("title").pluck(:id) + end + + test "order: allows symbol column name" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.order(:title).pluck(:id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_order(:title).pluck(:id) + end + + test "order: allows downcase symbol direction" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.order(title: :asc).pluck(:id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_order(title: :asc).pluck(:id) + end + + test "order: allows upcase symbol direction" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.order(title: :ASC).pluck(:id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_order(title: :ASC).pluck(:id) + end + + test "order: allows string direction" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.order(title: "asc").pluck(:id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_order(title: "asc").pluck(:id) + end + + test "order: allows multiple columns" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.order(:author_id, :title).pluck(:id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_order(:author_id, :title).pluck(:id) + end + + test "order: allows mixed" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.order(:author_id, title: :asc).pluck(:id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_order(:author_id, title: :asc).pluck(:id) + end + + test "order: disallows invalid column name" do + with_config(:disabled) do + assert_raises(ArgumentError) do + Post.order("title asc").pluck(:id) + end + end + end + + test "order: disallows invalid direction" do + with_config(:disabled) do + assert_raises(ArgumentError) do + Post.order(title: :foo).pluck(:id) + end + end + end + + test "order: disallows invalid column with direction" do + with_config(:disabled) do + assert_raises(ArgumentError) do + Post.order(foo: :asc).pluck(:id) + end + end + end + + test "pluck: allows string column name" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.pluck("title") + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_pluck("title") + end + + test "pluck: allows symbol column name" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.pluck(:title) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_pluck(:title) + end + + test "pluck: allows multiple column names" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.pluck(:title, :id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_pluck(:title, :id) + end + + test "pluck: allows column names with includes" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.includes(:comments).pluck(:title, :id) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.includes(:comments).unsafe_raw_pluck(:title, :id) + end + + test "pluck: allows auto-generated attributes" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.pluck(:tags_count) + end + + assert_equal enabled, disabled + assert_equal disabled, Post.unsafe_raw_pluck(:tags_count) + end + + test "pluck: disallows invalid column name" do + with_config(:disabled) do + assert_raises(ArgumentError) do + Post.pluck("length(title)") + end + end + end + + test "pluck: disallows invalid column name amongst valid names" do + with_config(:disabled) do + assert_raises(ArgumentError) do + Post.pluck(:title, "length(title)") + end + end + end + + test "pluck: disallows invalid column names with includes" do + with_config(:disabled) do + assert_raises(ArgumentError) do + Post.includes(:comments).pluck(:title, "length(title)") + end + end + end + + def with_configs(*new_values, &blk) + new_values.map { |nv| with_config(nv, &blk) } + end + + def with_config(new_value, &blk) + old_value = ActiveRecord::Base.allow_unsafe_raw_sql + ActiveRecord::Base.allow_unsafe_raw_sql = new_value + blk.call + ensure + ActiveRecord::Base.allow_unsafe_raw_sql = old_value + end +end -- cgit v1.2.3 From 864b16063d14977096d9d24ac894fee605dfb7a7 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Tue, 21 Feb 2017 11:17:16 -0700 Subject: allow Arel.sql() for pluck --- .../lib/active_record/attribute_methods.rb | 38 +++++++++++- activerecord/lib/active_record/errors.rb | 25 ++++++++ activerecord/lib/active_record/querying.rb | 21 +++---- .../lib/active_record/relation/calculations.rb | 20 +++++-- .../lib/active_record/relation/query_methods.rb | 54 +++-------------- activerecord/test/cases/calculations_test.rb | 24 ++++---- activerecord/test/cases/relation/merging_test.rb | 8 +-- activerecord/test/cases/unsafe_raw_sql_test.rb | 70 ++++++++++++++++------ 8 files changed, 159 insertions(+), 101 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index fa0d79ba5f..b3d3c0559f 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -167,6 +167,30 @@ module ActiveRecord end end + def enforce_raw_sql_whitelist(args, whitelist: attribute_names_and_aliases) # :nodoc: + return if allow_unsafe_raw_sql == :enabled + + unexpected = args.reject do |arg| + whitelist.include?(arg.to_s) || + arg.kind_of?(Arel::Node) || arg.is_a?(Arel::Nodes::SqlLiteral) + end + + return if unexpected.none? + + if allow_unsafe_raw_sql == :deprecated + ActiveSupport::Deprecation.warn( + "Dangerous query method used with non-attribute argument(s): " + + "#{unexpected.map(&:inspect).join(", ")}. Non-argument " + + "arguments will be disallowed in Rails 5.3." + ) + else + raise(ActiveRecord::UnknownAttributeReference, + "Query method called with non-attribute argument(s): " + + unexpected.map(&:inspect).join(", ") + ) + end + end + # Can the given name be treated as a column name? Returns true if name # is attribute or attribute alias. # @@ -178,7 +202,7 @@ module ActiveRecord # # Person.respond_to_attribute?("foo") # # => false - def respond_to_attribute?(name) + def respond_to_attribute?(name) # :nodoc: name = name.to_s attribute_names.include?(name) || attribute_aliases.include?(name) end @@ -214,6 +238,18 @@ module ActiveRecord ConnectionAdapters::NullColumn.new(name) end end + + # An Array of String attribute names and aliases for accessing those + # attributes. + # + # class Person < ActiveRecord::Base + # end + # + # Person.attribute_names_and_aliases + # # => ["id", "created_at", "updated_at", "name", "age"] + def attribute_names_and_aliases # :nodoc: + attribute_names + attribute_aliases.keys + end end # A Person object with a name attribute can ask person.respond_to?(:name), diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 9ef3316393..3c039eef82 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -339,4 +339,29 @@ module ActiveRecord # Wait time value is set by innodb_lock_wait_timeout. class TransactionTimeout < StatementInvalid end + + # UnknownAttributeReference is raised when an unknown and potentially unsafe + # value is passed to a query method when allow_unsafe_raw_sql is set to + # :disabled. For example, passing a non column name value to a relation's + # #order method might cause this exception. + # + # When working around this exception, caution should be taken to avoid SQL + # injection vulnerabilities when passing user-provided values to query + # methods. Known-safe values can be passed to query methods by wrapping them + # in Arel.sql. + # + # For example, with allow_unsafe_raw_sql set to :disabled, the following + # code would raise this exception: + # + # Post.order("length(title)").first + # + # The desired result can be accomplished by wrapping the known-safe string + # in Arel.sql: + # + # Post.order(Arel.sql("length(title)")).first + # + # Again, such a workaround should *not* be used when passing user-provided + # values, such as request parameters or model attributes to query methods. + class UnknownAttributeReference < ActiveRecordError + end end diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index 0233835bcf..3996d5661f 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -2,25 +2,18 @@ module ActiveRecord module Querying - delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, - :any?, :many?, :none?, :one?, to: :all - delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, - :fifth!, :forty_two, :forty_two!, :third_to_last, :third_to_last!, - :second_to_last, :second_to_last!, to: :all + delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :none?, :one?, to: :all + delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!, to: :all delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all - delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, - to: :all + delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all delegate :find_by, :find_by!, to: :all delegate :destroy_all, :delete_all, :update_all, to: :all delegate :find_each, :find_in_batches, :in_batches, to: :all - delegate :select, :group, :order, :unsafe_raw_order, :except, :reorder, - :unsafe_raw_reorder, :limit, :offset, :joins, :left_joins, - :left_outer_joins, :or, :where, :rewhere, :preload, :eager_load, - :includes, :from, :lock, :readonly, :extending, :having, - :create_with, :distinct, :references, :none, :unscope, :merge, - to: :all + delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or, + :where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly, :extending, + :having, :create_with, :distinct, :references, :none, :unscope, :merge, to: :all delegate :count, :average, :minimum, :maximum, :sum, :calculate, to: :all - delegate :pluck, :unsafe_raw_pluck, :ids, to: :all + delegate :pluck, :ids, to: :all # Executes a custom SQL query against your database and returns all the results. The results will # be returned as an array with columns requested encapsulated as attributes of the model you call diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index dea7542f03..236d36e15f 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -175,13 +175,21 @@ module ActiveRecord # See also #ids. # def pluck(*column_names) - _pluck(column_names, @klass.allow_unsafe_raw_sql == :enabled) - end + if loaded? && (column_names.map(&:to_s) - @klass.attribute_names_and_aliases).empty? + return records.pluck(*column_names) + end - # Same as #pluck but allows raw SQL regardless of `allow_unsafe_raw_sql` - # config setting. - def unsafe_raw_pluck(*column_names) - _pluck(column_names, true) + if has_include?(column_names.first) + construct_relation_for_association_calculations.pluck(*column_names) + else + enforce_raw_sql_whitelist(column_names) + relation = spawn + relation.select_values = column_names.map { |cn| + @klass.respond_to_attribute?(cn) ? arel_attribute(cn) : cn + } + result = klass.connection.select_all(relation.arel, nil, bound_attributes) + result.cast_values(klass.attribute_types) + end end # Pluck all the ID's for the relation using the table's primary key diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 63b1d8e154..4c63d0450a 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -295,22 +295,9 @@ module ActiveRecord spawn.order!(*args) end - # Same as #order but allows raw SQL regardless of `allow_unsafe_raw_sql` - # config setting. - def unsafe_raw_order(*args) # :nodoc: - check_if_method_has_arguments!(:order, args) - spawn.unsafe_raw_order!(*args) - end - # Same as #order but operates on relation in-place instead of copying. def order!(*args) # :nodoc: - restrict_order_args(args) unless klass.allow_unsafe_raw_sql == :enabled - unsafe_raw_order!(*args) - end - - # Same as #order! but allows raw SQL regardless of `allow_unsafe_raw_sql` - # config setting. - def unsafe_raw_order!(*args) # :nodoc: + enforce_raw_sql_whitelist(column_names_from_order_arguments(args)) preprocess_order_args(args) self.order_values += args @@ -331,22 +318,9 @@ module ActiveRecord spawn.reorder!(*args) end - # Same as #reorder but allows raw SQL regardless of `allow_unsafe_raw_sql` - # config setting. - def unsafe_raw_reorder(*args) # :nodoc: - check_if_method_has_arguments!(:reorder, args) - spawn.unsafe_raw_reorder!(*args) - end - # Same as #reorder but operates on relation in-place instead of copying. def reorder!(*args) # :nodoc: - restrict_order_args(args) unless klass.allow_unsafe_raw_sql == :enabled - unsafe_raw_reorder! - end - - # Same as #reorder! but allows raw SQL regardless of `allow_unsafe_raw_sql` - # config setting. - def unsafe_raw_reorder!(*args) # :nodoc: + enforce_raw_sql_whitelist(column_names_from_order_arguments(args)) preprocess_order_args(args) self.reordering_value = true @@ -946,6 +920,11 @@ module ActiveRecord private + # Extract column names from arguments passed to #order or #reorder. + def column_names_from_order_arguments(args) + args.flat_map { |arg| arg.is_a?(Hash) ? arg.keys : arg } + end + def assert_mutability! raise ImmutableRelation if @loaded raise ImmutableRelation if defined?(@arel) && @arel @@ -1169,25 +1148,6 @@ module ActiveRecord end.flatten! end - # Only allow column names and directions as arguments to #order and - # #reorder. Other arguments will cause an ArugmentError to be raised. - def restrict_order_args(args) - args = args.dup - orderings = args.extract_options! - columns = args | orderings.keys - - unrecognized = columns.reject { |c| klass.respond_to_attribute?(c) } - if unrecognized.any? - raise ArgumentError, "Invalid order column: #{unrecognized}" - end - - # TODO: find a better list of modifiers. - unrecognized = orderings.values.reject { |d| VALID_DIRECTIONS.include?(d.to_s) } - if unrecognized.any? - raise ArgumentError, "Invalid order direction: #{unrecognized}" - end - end - # Checks to make sure that the arguments are not blank. Note that if some # blank-like object were initially passed into the query method, then this # method will not raise an error. diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 66bc14b5ab..20dcb0080b 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -144,7 +144,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_should_order_by_calculation - c = Account.group(:firm_id).order("sum_credit_limit desc, firm_id").sum(:credit_limit) + c = Account.group(:firm_id).order(Arel.sql("sum_credit_limit desc, firm_id")).sum(:credit_limit) assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] } assert_equal [6, 2, 9, 1], c.keys.compact end @@ -644,7 +644,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_pluck_with_qualified_column_name - assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck("topics.id") + assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck(Arel.sql("topics.id")) end def test_pluck_auto_table_name_prefix @@ -659,18 +659,18 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_not_auto_table_name_prefix_if_column_joined Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)]) - assert_equal [7], Company.joins(:contracts).pluck(:developer_id) + assert_equal [7], Company.joins(:contracts).pluck(Arel.sql("developer_id")) end def test_pluck_with_selection_clause - assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT credit_limit").sort - assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT accounts.credit_limit").sort - assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT(credit_limit)").sort + assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT credit_limit")).sort + assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT accounts.credit_limit")).sort + assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT(credit_limit)")).sort # MySQL returns "SUM(DISTINCT(credit_limit))" as the column name unless # an alias is provided. Without the alias, the column cannot be found # and properly typecast. - assert_equal [50 + 53 + 55 + 60], Account.pluck("SUM(DISTINCT(credit_limit)) as credit_limit") + assert_equal [50 + 53 + 55 + 60], Account.pluck(Arel.sql("SUM(DISTINCT(credit_limit)) as credit_limit")) end def test_plucks_with_ids @@ -684,7 +684,7 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_not_auto_table_name_prefix_if_column_included Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)]) - ids = Company.includes(:contracts).pluck(:developer_id) + ids = Company.includes(:contracts).pluck(Arel.sql("developer_id")) assert_equal Company.count, ids.length assert_equal [7], ids.compact end @@ -704,12 +704,12 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_with_multiple_columns_and_selection_clause assert_equal [[1, 50], [2, 50], [3, 50], [4, 60], [5, 55], [6, 53]], - Account.pluck("id, credit_limit") + Account.pluck(Arel.sql("id, credit_limit")) end def test_pluck_with_multiple_columns_and_includes Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)]) - companies_and_developers = Company.order("companies.id").includes(:contracts).pluck(:name, :developer_id) + companies_and_developers = Company.order(Arel.sql("companies.id")).includes(:contracts).pluck(:name, Arel.sql("developer_id")) assert_equal Company.count, companies_and_developers.length assert_equal ["37signals", nil], companies_and_developers.first @@ -731,7 +731,7 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_columns_with_same_name expected = [["The First Topic", "The Second Topic of the day"], ["The Third Topic of the day", "The Fourth Topic of the day"]] actual = Topic.joins(:replies) - .pluck("topics.title", "replies_topics.title") + .pluck(Arel.sql("topics.title"), Arel.sql("replies_topics.title")) assert_equal expected, actual end @@ -772,7 +772,7 @@ class CalculationsTest < ActiveRecord::TestCase companies = Company.order(:name).limit(3).load assert_queries 1 do - assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck("DISTINCT name") + assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck(Arel.sql("DISTINCT name")) end end diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index b68b3723f6..953e0fee76 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -118,7 +118,7 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase test "merging where relations" do hello_by_bob = Post.where(body: "hello").joins(:author). - merge(Author.where(name: "Bob")).order("posts.id").pluck("posts.id") + merge(Author.where(name: "Bob")).order("posts.id").pluck(Arel.sql("posts.id")) assert_equal [posts(:misc_by_bob).id, posts(:other_by_bob).id], hello_by_bob @@ -126,19 +126,19 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase test "merging order relations" do posts_by_author_name = Post.limit(3).joins(:author). - merge(Author.order(:name)).pluck("authors.name") + merge(Author.order(:name)).pluck(Arel.sql("authors.name")) assert_equal ["Bob", "Bob", "David"], posts_by_author_name posts_by_author_name = Post.limit(3).joins(:author). - merge(Author.order("name")).pluck("authors.name") + merge(Author.order("name")).pluck(Arel.sql("authors.name")) assert_equal ["Bob", "Bob", "David"], posts_by_author_name end test "merging order relations (using a hash argument)" do posts_by_author_name = Post.limit(4).joins(:author). - merge(Author.order(name: :desc)).pluck("authors.name") + merge(Author.order(name: :desc)).pluck(Arel.sql("authors.name")) assert_equal ["Mary", "Mary", "Mary", "David"], posts_by_author_name end diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb index d05f0f12d9..89eb02594a 100644 --- a/activerecord/test/cases/unsafe_raw_sql_test.rb +++ b/activerecord/test/cases/unsafe_raw_sql_test.rb @@ -13,7 +13,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_order("title").pluck(:id) + assert_equal disabled, Post.order(Arel.sql("title")).pluck(:id) end test "order: allows symbol column name" do @@ -22,7 +22,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_order(:title).pluck(:id) + assert_equal disabled, Post.order(Arel.sql("title")).pluck(:id) end test "order: allows downcase symbol direction" do @@ -31,7 +31,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_order(title: :asc).pluck(:id) + assert_equal disabled, Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id) end test "order: allows upcase symbol direction" do @@ -40,7 +40,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_order(title: :ASC).pluck(:id) + assert_equal disabled, Post.order(Arel.sql("title") => Arel.sql("ASC")).pluck(:id) end test "order: allows string direction" do @@ -49,7 +49,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_order(title: "asc").pluck(:id) + assert_equal disabled, Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id) end test "order: allows multiple columns" do @@ -58,7 +58,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_order(:author_id, :title).pluck(:id) + assert_equal disabled, Post.order(Arel.sql("author_id"), Arel.sql("title")).pluck(:id) end test "order: allows mixed" do @@ -67,12 +67,12 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_order(:author_id, title: :asc).pluck(:id) + assert_equal disabled, Post.order(Arel.sql("author_id"), Arel.sql("title") => Arel.sql("asc")).pluck(:id) end test "order: disallows invalid column name" do with_config(:disabled) do - assert_raises(ArgumentError) do + assert_raises(ActiveRecord::UnknownAttributeReference) do Post.order("title asc").pluck(:id) end end @@ -88,19 +88,37 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "order: disallows invalid column with direction" do with_config(:disabled) do - assert_raises(ArgumentError) do + assert_raises(ActiveRecord::UnknownAttributeReference) do Post.order(foo: :asc).pluck(:id) end end end + test "order: always allows Arel" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.order(Arel.sql("length(title)")).pluck(:title) + end + + assert_equal enabled, disabled + end + + test "order: logs deprecation warning for unrecognized column" do + with_config(:deprecated) do + ActiveSupport::Deprecation.expects(:warn).with do |msg| + msg =~ /\ADangerous query method used with .*length\(title\)/ + end + + Post.order("length(title)") + end + end + test "pluck: allows string column name" do enabled, disabled = with_configs(:enabled, :disabled) do Post.pluck("title") end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_pluck("title") + assert_equal disabled, Post.pluck(Arel.sql("title")) end test "pluck: allows symbol column name" do @@ -109,7 +127,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_pluck(:title) + assert_equal disabled, Post.pluck(Arel.sql("title")) end test "pluck: allows multiple column names" do @@ -118,7 +136,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_pluck(:title, :id) + assert_equal disabled, Post.pluck(Arel.sql("title"), Arel.sql("id")) end test "pluck: allows column names with includes" do @@ -127,7 +145,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.includes(:comments).unsafe_raw_pluck(:title, :id) + assert_equal disabled, Post.includes(:comments).pluck(Arel.sql("title"), Arel.sql("id")) end test "pluck: allows auto-generated attributes" do @@ -136,12 +154,12 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end assert_equal enabled, disabled - assert_equal disabled, Post.unsafe_raw_pluck(:tags_count) + assert_equal disabled, Post.pluck(Arel.sql("tags_count")) end test "pluck: disallows invalid column name" do with_config(:disabled) do - assert_raises(ArgumentError) do + assert_raises(ActiveRecord::UnknownAttributeReference) do Post.pluck("length(title)") end end @@ -149,7 +167,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "pluck: disallows invalid column name amongst valid names" do with_config(:disabled) do - assert_raises(ArgumentError) do + assert_raises(ActiveRecord::UnknownAttributeReference) do Post.pluck(:title, "length(title)") end end @@ -157,12 +175,30 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "pluck: disallows invalid column names with includes" do with_config(:disabled) do - assert_raises(ArgumentError) do + assert_raises(ActiveRecord::UnknownAttributeReference) do Post.includes(:comments).pluck(:title, "length(title)") end end end + test "pluck: always allows Arel" do + enabled, disabled = with_configs(:enabled, :disabled) do + Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) + end + + assert_equal enabled, disabled + end + + test "pluck: logs deprecation warning" do + with_config(:deprecated) do + ActiveSupport::Deprecation.expects(:warn).with do |msg| + msg =~ /\ADangerous query method used with .*length\(title\)/ + end + + Post.includes(:comments).pluck(:title, "length(title)") + end + end + def with_configs(*new_values, &blk) new_values.map { |nv| with_config(nv, &blk) } end -- cgit v1.2.3 From 0cf5f2f048ab547edb0d017095a642126a87a879 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Tue, 8 Aug 2017 13:58:07 -0600 Subject: make tests more verbose/explicit --- activerecord/test/cases/unsafe_raw_sql_test.rb | 174 ++++++++++++++----------- 1 file changed, 96 insertions(+), 78 deletions(-) diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb index 89eb02594a..53418bb914 100644 --- a/activerecord/test/cases/unsafe_raw_sql_test.rb +++ b/activerecord/test/cases/unsafe_raw_sql_test.rb @@ -8,70 +8,77 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase fixtures :posts, :comments test "order: allows string column name" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.order("title").pluck(:id) - end + ids_expected = Post.order(Arel.sql("title")).pluck(:id) + + ids_enabled = with_unsafe_raw_sql_enabled { Post.order("title").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("title").pluck(:id) } - assert_equal enabled, disabled - assert_equal disabled, Post.order(Arel.sql("title")).pluck(:id) + assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_disabled end test "order: allows symbol column name" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.order(:title).pluck(:id) - end + ids_expected = Post.order(Arel.sql("title")).pluck(:id) + + ids_enabled = with_unsafe_raw_sql_enabled { Post.order(:title).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:title).pluck(:id) } - assert_equal enabled, disabled - assert_equal disabled, Post.order(Arel.sql("title")).pluck(:id) + assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_disabled end test "order: allows downcase symbol direction" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.order(title: :asc).pluck(:id) - end + ids_expected = Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id) + + ids_enabled = with_unsafe_raw_sql_enabled { Post.order(title: :asc).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: :asc).pluck(:id) } - assert_equal enabled, disabled - assert_equal disabled, Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id) + assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_disabled end test "order: allows upcase symbol direction" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.order(title: :ASC).pluck(:id) - end + ids_expected = Post.order(Arel.sql("title") => Arel.sql("ASC")).pluck(:id) + + ids_enabled = with_unsafe_raw_sql_enabled { Post.order(title: :ASC).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: :ASC).pluck(:id) } - assert_equal enabled, disabled - assert_equal disabled, Post.order(Arel.sql("title") => Arel.sql("ASC")).pluck(:id) + assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_disabled end test "order: allows string direction" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.order(title: "asc").pluck(:id) - end + ids_expected = Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id) + + ids_enabled = with_unsafe_raw_sql_enabled { Post.order(title: "asc").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: "asc").pluck(:id) } - assert_equal enabled, disabled - assert_equal disabled, Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id) + assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_disabled end test "order: allows multiple columns" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.order(:author_id, :title).pluck(:id) - end + ids_expected = Post.order(Arel.sql("author_id"), Arel.sql("title")).pluck(:id) + + ids_enabled = with_unsafe_raw_sql_enabled { Post.order(:author_id, :title).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:author_id, :title).pluck(:id) } - assert_equal enabled, disabled - assert_equal disabled, Post.order(Arel.sql("author_id"), Arel.sql("title")).pluck(:id) + assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_disabled end test "order: allows mixed" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.order(:author_id, title: :asc).pluck(:id) - end + ids_expected = Post.order(Arel.sql("author_id"), Arel.sql("title") => Arel.sql("asc")).pluck(:id) + + ids_enabled = with_unsafe_raw_sql_enabled { Post.order(:author_id, title: :asc).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:author_id, title: :asc).pluck(:id) } - assert_equal enabled, disabled - assert_equal disabled, Post.order(Arel.sql("author_id"), Arel.sql("title") => Arel.sql("asc")).pluck(:id) + assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_disabled end test "order: disallows invalid column name" do - with_config(:disabled) do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.order("title asc").pluck(:id) end @@ -79,7 +86,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "order: disallows invalid direction" do - with_config(:disabled) do + with_unsafe_raw_sql_disabled do assert_raises(ArgumentError) do Post.order(title: :foo).pluck(:id) end @@ -87,7 +94,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "order: disallows invalid column with direction" do - with_config(:disabled) do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.order(foo: :asc).pluck(:id) end @@ -95,15 +102,14 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "order: always allows Arel" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.order(Arel.sql("length(title)")).pluck(:title) - end + ids_enabled = with_unsafe_raw_sql_enabled { Post.order(Arel.sql("length(title)")).pluck(:title) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("length(title)")).pluck(:title) } - assert_equal enabled, disabled + assert_equal ids_enabled, ids_disabled end test "order: logs deprecation warning for unrecognized column" do - with_config(:deprecated) do + with_unsafe_raw_sql_deprecated do ActiveSupport::Deprecation.expects(:warn).with do |msg| msg =~ /\ADangerous query method used with .*length\(title\)/ end @@ -113,52 +119,57 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "pluck: allows string column name" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.pluck("title") - end + titles_expected = Post.pluck(Arel.sql("title")) + + titles_enabled = with_unsafe_raw_sql_enabled { Post.pluck("title") } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("title") } - assert_equal enabled, disabled - assert_equal disabled, Post.pluck(Arel.sql("title")) + assert_equal titles_expected, titles_enabled + assert_equal titles_expected, titles_disabled end test "pluck: allows symbol column name" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.pluck(:title) - end + titles_expected = Post.pluck(Arel.sql("title")) + + titles_enabled = with_unsafe_raw_sql_enabled { Post.pluck(:title) } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:title) } - assert_equal enabled, disabled - assert_equal disabled, Post.pluck(Arel.sql("title")) + assert_equal titles_expected, titles_enabled + assert_equal titles_expected, titles_disabled end test "pluck: allows multiple column names" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.pluck(:title, :id) - end + values_expected = Post.pluck(Arel.sql("title"), Arel.sql("id")) + + values_enabled = with_unsafe_raw_sql_enabled { Post.pluck(:title, :id) } + values_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:title, :id) } - assert_equal enabled, disabled - assert_equal disabled, Post.pluck(Arel.sql("title"), Arel.sql("id")) + assert_equal values_expected, values_enabled + assert_equal values_expected, values_disabled end test "pluck: allows column names with includes" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.includes(:comments).pluck(:title, :id) - end + values_expected = Post.includes(:comments).pluck(Arel.sql("title"), Arel.sql("id")) + + values_enabled = with_unsafe_raw_sql_enabled { Post.includes(:comments).pluck(:title, :id) } + values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, :id) } - assert_equal enabled, disabled - assert_equal disabled, Post.includes(:comments).pluck(Arel.sql("title"), Arel.sql("id")) + assert_equal values_expected, values_enabled + assert_equal values_expected, values_disabled end test "pluck: allows auto-generated attributes" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.pluck(:tags_count) - end + values_expected = Post.pluck(Arel.sql("tags_count")) + + values_enabled = with_unsafe_raw_sql_enabled { Post.pluck(:tags_count) } + values_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:tags_count) } - assert_equal enabled, disabled - assert_equal disabled, Post.pluck(Arel.sql("tags_count")) + assert_equal values_expected, values_enabled + assert_equal values_expected, values_disabled end test "pluck: disallows invalid column name" do - with_config(:disabled) do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.pluck("length(title)") end @@ -166,7 +177,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "pluck: disallows invalid column name amongst valid names" do - with_config(:disabled) do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.pluck(:title, "length(title)") end @@ -174,7 +185,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "pluck: disallows invalid column names with includes" do - with_config(:disabled) do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.includes(:comments).pluck(:title, "length(title)") end @@ -182,15 +193,14 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "pluck: always allows Arel" do - enabled, disabled = with_configs(:enabled, :disabled) do - Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) - end + values_enabled = with_unsafe_raw_sql_enabled { Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) } + values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) } - assert_equal enabled, disabled + assert_equal values_enabled, values_disabled end test "pluck: logs deprecation warning" do - with_config(:deprecated) do + with_unsafe_raw_sql_deprecated do ActiveSupport::Deprecation.expects(:warn).with do |msg| msg =~ /\ADangerous query method used with .*length\(title\)/ end @@ -199,8 +209,16 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end end - def with_configs(*new_values, &blk) - new_values.map { |nv| with_config(nv, &blk) } + def with_unsafe_raw_sql_enabled(&blk) + with_config(:enabled, &blk) + end + + def with_unsafe_raw_sql_disabled(&blk) + with_config(:disabled, &blk) + end + + def with_unsafe_raw_sql_deprecated(&blk) + with_config(:deprecated, &blk) end def with_config(new_value, &blk) -- cgit v1.2.3 From 310c3a8f2d043f3d00d3f703052a1e160430a2c2 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Thu, 10 Aug 2017 11:21:22 -0600 Subject: beef up deprecation warning --- activerecord/lib/active_record/attribute_methods.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index b3d3c0559f..e167c4ad6f 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -179,9 +179,13 @@ module ActiveRecord if allow_unsafe_raw_sql == :deprecated ActiveSupport::Deprecation.warn( - "Dangerous query method used with non-attribute argument(s): " + - "#{unexpected.map(&:inspect).join(", ")}. Non-argument " + - "arguments will be disallowed in Rails 5.3." + "Dangerous query method (method whose arguments are used as raw " \ + "SQL) called with non-attribute argument(s): " \ + "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \ + "arguments will be disallowed in Rails 6.0. This method should " \ + "not be called with user-provided values, such as request " \ + "parameters or model attributes. Known-safe values can be passed " \ + "by wrapping them in Arel.sql()." ) else raise(ActiveRecord::UnknownAttributeReference, -- cgit v1.2.3 From 0d8626ad3f1bac3b814d554147ff7926c0380fad Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Fri, 11 Aug 2017 09:55:54 -0600 Subject: remove :enabled option --- .../lib/active_record/attribute_methods.rb | 2 - activerecord/lib/active_record/core.rb | 5 +- activerecord/test/cases/unsafe_raw_sql_test.rb | 104 ++++++++++----------- 3 files changed, 52 insertions(+), 59 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index e167c4ad6f..5ce3ba7a63 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -168,8 +168,6 @@ module ActiveRecord end def enforce_raw_sql_whitelist(args, whitelist: attribute_names_and_aliases) # :nodoc: - return if allow_unsafe_raw_sql == :enabled - unexpected = args.reject do |arg| whitelist.include?(arg.to_s) || arg.kind_of?(Arel::Node) || arg.is_a?(Arel::Nodes::SqlLiteral) diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index b1e3f71dfe..b97b14644e 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -78,12 +78,11 @@ module ActiveRecord # :singleton-method: # Specify the behavior for unsafe raw query methods. Values are as follows - # enabled - Unsafe raw SQL can be passed to query methods. # deprecated - Warnings are logged when unsafe raw SQL is passed to # query methods. # disabled - Unsafe raw SQL passed to query methods results in - # ArguementError. - mattr_accessor :allow_unsafe_raw_sql, instance_writer: false, default: :enabled + # UnknownAttributeReference exception. + mattr_accessor :allow_unsafe_raw_sql, instance_writer: false, default: :deprecated ## # :singleton-method: diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb index 53418bb914..74471b0ace 100644 --- a/activerecord/test/cases/unsafe_raw_sql_test.rb +++ b/activerecord/test/cases/unsafe_raw_sql_test.rb @@ -10,75 +10,75 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "order: allows string column name" do ids_expected = Post.order(Arel.sql("title")).pluck(:id) - ids_enabled = with_unsafe_raw_sql_enabled { Post.order("title").pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order("title").pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order("title").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("title").pluck(:id) } - assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end test "order: allows symbol column name" do ids_expected = Post.order(Arel.sql("title")).pluck(:id) - ids_enabled = with_unsafe_raw_sql_enabled { Post.order(:title).pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:title).pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(:title).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:title).pluck(:id) } - assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end test "order: allows downcase symbol direction" do ids_expected = Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id) - ids_enabled = with_unsafe_raw_sql_enabled { Post.order(title: :asc).pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: :asc).pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(title: :asc).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: :asc).pluck(:id) } - assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end test "order: allows upcase symbol direction" do ids_expected = Post.order(Arel.sql("title") => Arel.sql("ASC")).pluck(:id) - ids_enabled = with_unsafe_raw_sql_enabled { Post.order(title: :ASC).pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: :ASC).pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(title: :ASC).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: :ASC).pluck(:id) } - assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end test "order: allows string direction" do ids_expected = Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id) - ids_enabled = with_unsafe_raw_sql_enabled { Post.order(title: "asc").pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: "asc").pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(title: "asc").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: "asc").pluck(:id) } - assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end test "order: allows multiple columns" do ids_expected = Post.order(Arel.sql("author_id"), Arel.sql("title")).pluck(:id) - ids_enabled = with_unsafe_raw_sql_enabled { Post.order(:author_id, :title).pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:author_id, :title).pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(:author_id, :title).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:author_id, :title).pluck(:id) } - assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end test "order: allows mixed" do ids_expected = Post.order(Arel.sql("author_id"), Arel.sql("title") => Arel.sql("asc")).pluck(:id) - ids_enabled = with_unsafe_raw_sql_enabled { Post.order(:author_id, title: :asc).pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:author_id, title: :asc).pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(:author_id, title: :asc).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:author_id, title: :asc).pluck(:id) } - assert_equal ids_expected, ids_enabled + assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end test "order: disallows invalid column name" do - with_unsafe_raw_sql_disabled do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.order("title asc").pluck(:id) end @@ -86,7 +86,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "order: disallows invalid direction" do - with_unsafe_raw_sql_disabled do + with_unsafe_raw_sql_disabled do assert_raises(ArgumentError) do Post.order(title: :foo).pluck(:id) end @@ -94,7 +94,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "order: disallows invalid column with direction" do - with_unsafe_raw_sql_disabled do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.order(foo: :asc).pluck(:id) end @@ -102,16 +102,16 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "order: always allows Arel" do - ids_enabled = with_unsafe_raw_sql_enabled { Post.order(Arel.sql("length(title)")).pluck(:title) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("length(title)")).pluck(:title) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("length(title)")).pluck(:title) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("length(title)")).pluck(:title) } - assert_equal ids_enabled, ids_disabled + assert_equal ids_depr, ids_disabled end test "order: logs deprecation warning for unrecognized column" do with_unsafe_raw_sql_deprecated do ActiveSupport::Deprecation.expects(:warn).with do |msg| - msg =~ /\ADangerous query method used with .*length\(title\)/ + msg =~ /\ADangerous query method/ end Post.order("length(title)") @@ -121,55 +121,55 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "pluck: allows string column name" do titles_expected = Post.pluck(Arel.sql("title")) - titles_enabled = with_unsafe_raw_sql_enabled { Post.pluck("title") } - titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("title") } + titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("title") } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("title") } - assert_equal titles_expected, titles_enabled + assert_equal titles_expected, titles_depr assert_equal titles_expected, titles_disabled end test "pluck: allows symbol column name" do titles_expected = Post.pluck(Arel.sql("title")) - titles_enabled = with_unsafe_raw_sql_enabled { Post.pluck(:title) } - titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:title) } + titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck(:title) } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:title) } - assert_equal titles_expected, titles_enabled + assert_equal titles_expected, titles_depr assert_equal titles_expected, titles_disabled end test "pluck: allows multiple column names" do values_expected = Post.pluck(Arel.sql("title"), Arel.sql("id")) - values_enabled = with_unsafe_raw_sql_enabled { Post.pluck(:title, :id) } - values_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:title, :id) } + values_depr = with_unsafe_raw_sql_deprecated { Post.pluck(:title, :id) } + values_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:title, :id) } - assert_equal values_expected, values_enabled + assert_equal values_expected, values_depr assert_equal values_expected, values_disabled end test "pluck: allows column names with includes" do values_expected = Post.includes(:comments).pluck(Arel.sql("title"), Arel.sql("id")) - values_enabled = with_unsafe_raw_sql_enabled { Post.includes(:comments).pluck(:title, :id) } - values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, :id) } + values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, :id) } + values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, :id) } - assert_equal values_expected, values_enabled + assert_equal values_expected, values_depr assert_equal values_expected, values_disabled end test "pluck: allows auto-generated attributes" do values_expected = Post.pluck(Arel.sql("tags_count")) - values_enabled = with_unsafe_raw_sql_enabled { Post.pluck(:tags_count) } - values_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:tags_count) } + values_depr = with_unsafe_raw_sql_deprecated { Post.pluck(:tags_count) } + values_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:tags_count) } - assert_equal values_expected, values_enabled + assert_equal values_expected, values_depr assert_equal values_expected, values_disabled end test "pluck: disallows invalid column name" do - with_unsafe_raw_sql_disabled do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.pluck("length(title)") end @@ -177,7 +177,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "pluck: disallows invalid column name amongst valid names" do - with_unsafe_raw_sql_disabled do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.pluck(:title, "length(title)") end @@ -185,7 +185,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "pluck: disallows invalid column names with includes" do - with_unsafe_raw_sql_disabled do + with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do Post.includes(:comments).pluck(:title, "length(title)") end @@ -193,26 +193,22 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "pluck: always allows Arel" do - values_enabled = with_unsafe_raw_sql_enabled { Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) } - values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) } + values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) } + values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) } - assert_equal values_enabled, values_disabled + assert_equal values_depr, values_disabled end test "pluck: logs deprecation warning" do with_unsafe_raw_sql_deprecated do ActiveSupport::Deprecation.expects(:warn).with do |msg| - msg =~ /\ADangerous query method used with .*length\(title\)/ + msg =~ /\ADangerous query method/ end Post.includes(:comments).pluck(:title, "length(title)") end end - def with_unsafe_raw_sql_enabled(&blk) - with_config(:enabled, &blk) - end - def with_unsafe_raw_sql_disabled(&blk) with_config(:disabled, &blk) end -- cgit v1.2.3 From 92e78593ee056bb73a7a87c10af3f2587eca1150 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Mon, 25 Sep 2017 10:13:53 -0600 Subject: work with actual string when reversing order --- activerecord/lib/active_record/relation/query_methods.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4c63d0450a..7fe0129b4a 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1075,6 +1075,9 @@ module ActiveRecord when Arel::Nodes::Ordering o.reverse when String + # ensure we're not dealing with string subclass (Eg. Arel::Nodes::SqlLiteral) + o = String.new(o) + if does_not_support_reverse?(o) raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically" end -- cgit v1.2.3 From 65328025917f0b60d0fbbeca87f173f34d9c91c5 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Mon, 25 Sep 2017 10:39:35 -0600 Subject: call enforce_raw_sql_whitelist on @klass so it works with FakeKlass --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 7fe0129b4a..f3b44d19d6 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -297,7 +297,7 @@ module ActiveRecord # Same as #order but operates on relation in-place instead of copying. def order!(*args) # :nodoc: - enforce_raw_sql_whitelist(column_names_from_order_arguments(args)) + @klass.enforce_raw_sql_whitelist(column_names_from_order_arguments(args)) preprocess_order_args(args) self.order_values += args @@ -320,7 +320,7 @@ module ActiveRecord # Same as #reorder but operates on relation in-place instead of copying. def reorder!(*args) # :nodoc: - enforce_raw_sql_whitelist(column_names_from_order_arguments(args)) + @klass.enforce_raw_sql_whitelist(column_names_from_order_arguments(args)) preprocess_order_args(args) self.reordering_value = true -- cgit v1.2.3 From 40d302d880f247ef9547708b1d26a390945b6fe9 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Mon, 25 Sep 2017 10:54:53 -0600 Subject: always allow Arel::Attributes::Attribute also --- activerecord/lib/active_record/attribute_methods.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 5ce3ba7a63..ff381b4e0b 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -170,7 +170,9 @@ module ActiveRecord def enforce_raw_sql_whitelist(args, whitelist: attribute_names_and_aliases) # :nodoc: unexpected = args.reject do |arg| whitelist.include?(arg.to_s) || - arg.kind_of?(Arel::Node) || arg.is_a?(Arel::Nodes::SqlLiteral) + arg.kind_of?(Arel::Node) || + arg.is_a?(Arel::Nodes::SqlLiteral) || + arg.is_a?(Arel::Attributes::Attribute) end return if unexpected.none? -- cgit v1.2.3 From 02a17492a937d4b423590644ad1481b82facd394 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Mon, 25 Sep 2017 10:58:08 -0600 Subject: work around deprecation warnings in a bunch of tests --- .../associations/cascaded_eager_loading_test.rb | 32 +++--- .../associations/eager_load_nested_include_test.rb | 2 +- activerecord/test/cases/associations/eager_test.rb | 104 ++++++++--------- .../has_and_belongs_to_many_associations_test.rb | 2 +- .../associations/has_many_associations_test.rb | 10 +- .../has_many_through_associations_test.rb | 4 +- .../has_one_through_associations_test.rb | 6 +- .../associations/inverse_associations_test.rb | 8 +- .../test/cases/associations/join_model_test.rb | 32 +++--- .../nested_through_associations_test.rb | 30 ++--- activerecord/test/cases/base_test.rb | 24 ++-- activerecord/test/cases/batches_test.rb | 14 +-- activerecord/test/cases/finder_test.rb | 40 +++---- activerecord/test/cases/relation/merging_test.rb | 14 +-- activerecord/test/cases/relation/mutation_test.rb | 2 +- activerecord/test/cases/relation/or_test.rb | 14 +-- activerecord/test/cases/relations_test.rb | 125 +++++++++++---------- .../test/cases/scoping/default_scoping_test.rb | 62 +++++----- .../test/cases/scoping/named_scoping_test.rb | 4 +- .../test/cases/scoping/relation_scoping_test.rb | 16 +-- activerecord/test/models/author.rb | 24 ++-- activerecord/test/models/car.rb | 6 +- activerecord/test/models/category.rb | 2 +- activerecord/test/models/comment.rb | 2 +- activerecord/test/models/company.rb | 2 +- activerecord/test/models/company_in_module.rb | 2 +- activerecord/test/models/developer.rb | 22 ++-- activerecord/test/models/membership.rb | 2 +- activerecord/test/models/owner.rb | 2 +- activerecord/test/models/person.rb | 2 +- activerecord/test/models/pirate.rb | 4 +- activerecord/test/models/post.rb | 12 +- activerecord/test/models/project.rb | 4 +- activerecord/test/models/tag.rb | 2 +- 34 files changed, 320 insertions(+), 313 deletions(-) diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index e69cfe5e52..3eba5ed466 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -18,7 +18,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase :categorizations, :people, :categories, :edges, :vertices def test_eager_association_loading_with_cascaded_two_levels - authors = Author.all.merge!(includes: { posts: :comments }, order: "authors.id").to_a + authors = Author.all.merge!(includes: { posts: :comments }, order: Arel.sql("authors.id")).to_a assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal 3, authors[1].posts.size @@ -26,7 +26,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_cascaded_two_levels_and_one_level - authors = Author.all.merge!(includes: [{ posts: :comments }, :categorizations], order: "authors.id").to_a + authors = Author.all.merge!(includes: [{ posts: :comments }, :categorizations], order: Arel.sql("authors.id")).to_a assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal 3, authors[1].posts.size @@ -42,7 +42,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_grafts_stashed_associations_to_correct_parent - assert_equal people(:michael), Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order("people.id").first + assert_equal people(:michael), Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order(Arel.sql("people.id")).first end def test_cascaded_eager_association_loading_with_join_for_count @@ -78,7 +78,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations - authors = Author.all.merge!(includes: { posts: [:comments, :categorizations] }, order: "authors.id").to_a + authors = Author.all.merge!(includes: { posts: [:comments, :categorizations] }, order: Arel.sql("authors.id")).to_a assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal 3, authors[1].posts.size @@ -86,7 +86,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference - authors = Author.all.merge!(includes: { posts: [:comments, :author] }, order: "authors.id").to_a + authors = Author.all.merge!(includes: { posts: [:comments, :author] }, order: Arel.sql("authors.id")).to_a assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal authors(:david).name, authors[0].name @@ -94,13 +94,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_cascaded_two_levels_with_condition - authors = Author.all.merge!(includes: { posts: :comments }, where: "authors.id=1", order: "authors.id").to_a + authors = Author.all.merge!(includes: { posts: :comments }, where: "authors.id=1", order: Arel.sql("authors.id")).to_a assert_equal 1, authors.size assert_equal 5, authors[0].posts.size end def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong - firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: "companies.id").to_a + firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: Arel.sql("companies.id")).to_a assert_equal 2, firms.size assert_equal firms.first.account, firms.first.account.firm.account assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account } @@ -108,7 +108,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_has_many_sti - topics = Topic.all.merge!(includes: :replies, order: "topics.id").to_a + topics = Topic.all.merge!(includes: :replies, order: Arel.sql("topics.id")).to_a first, second, = topics(:first).replies.size, topics(:second).replies.size assert_no_queries do assert_equal first, topics[0].replies.size @@ -121,7 +121,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase silly.parent_id = 1 assert silly.save - topics = Topic.all.merge!(includes: :replies, order: ["topics.id", "replies_topics.id"]).to_a + topics = Topic.all.merge!(includes: :replies, order: [Arel.sql("topics.id"), Arel.sql("replies_topics.id")]).to_a assert_no_queries do assert_equal 2, topics[0].replies.size assert_equal 0, topics[1].replies.size @@ -129,14 +129,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_belongs_to_sti - replies = Reply.all.merge!(includes: :topic, order: "topics.id").to_a + replies = Reply.all.merge!(includes: :topic, order: Arel.sql("topics.id")).to_a assert_includes replies, topics(:second) assert_not_includes replies, topics(:first) assert_equal topics(:first), assert_no_queries { replies.first.topic } end def test_eager_association_loading_with_multiple_stis_and_order - author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first + author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: [Arel.sql("authors.name"), Arel.sql("comments.body"), Arel.sql("very_special_comments_posts.body")], where: "posts.id = 4").first assert_equal authors(:david), author assert_no_queries do author.posts.first.special_comments @@ -145,7 +145,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_of_stis_with_multiple_references - authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: "comments.body, very_special_comments_posts.body", where: "posts.id = 4").to_a + authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: Arel.sql("comments.body, very_special_comments_posts.body"), where: "posts.id = 4").to_a assert_equal [authors(:david)], authors assert_no_queries do authors.first.posts.first.special_comments.first.post.special_comments @@ -154,7 +154,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_where_first_level_returns_nil - authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: "authors.id DESC").to_a + authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: Arel.sql("authors.id DESC")).to_a assert_equal [authors(:bob), authors(:mary), authors(:david)], authors assert_no_queries do authors[2].post_about_thinking.comments.first @@ -162,17 +162,17 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through - source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: "vertices.id").first + source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: Arel.sql("vertices.id")).first assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first } end def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many - sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: "vertices.id DESC").first + sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: Arel.sql("vertices.id DESC")).first assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first } end def test_eager_association_loading_with_cascaded_interdependent_one_level_and_two_levels - authors_relation = Author.all.merge!(includes: [:comments, { posts: :categorizations }], order: "authors.id") + authors_relation = Author.all.merge!(includes: [:comments, { posts: :categorizations }], order: Arel.sql("authors.id")) authors = authors_relation.to_a assert_equal 3, authors.size assert_equal 10, authors[0].comments.size 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 c5b2b77bd4..b1809401fb 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -120,7 +120,7 @@ class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase assert_nothing_raised do # @davey_mcdave doesn't have any author_favorites includes = { posts: :comments, categorizations: :category, author_favorites: :favorite_author } - Author.all.merge!(includes: includes, where: { authors: { name: @davey_mcdave.name } }, order: "categories.name").to_a + Author.all.merge!(includes: includes, where: { authors: { name: @davey_mcdave.name } }, order: Arel.sql("categories.name")).to_a end end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 9afe6a893c..d5ca87900e 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -62,7 +62,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_loading_with_one_association_with_non_preload - posts = Post.all.merge!(includes: :last_comment, order: "comments.id DESC").to_a + posts = Post.all.merge!(includes: :last_comment, order: Arel.sql("comments.id DESC")).to_a post = posts.find { |p| p.id == 1 } assert_equal Post.find(1).last_comment, post.last_comment end @@ -82,7 +82,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_with_ordering - list = Post.all.merge!(includes: :comments, order: "posts.id DESC").to_a + list = Post.all.merge!(includes: :comments, order: Arel.sql("posts.id DESC")).to_a [:other_by_mary, :other_by_bob, :misc_by_mary, :misc_by_bob, :eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments, :authorless, :thinking, :welcome ].each_with_index do |post, index| @@ -108,12 +108,12 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_with_two_tables_in_from_without_getting_double_quoted - posts = Post.select("posts.*").from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").order("posts.id").to_a + posts = Post.select("posts.*").from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").order(Arel.sql("posts.id")).to_a assert_equal 2, posts.first.comments.size end def test_loading_with_multiple_associations - posts = Post.all.merge!(includes: [ :comments, :author, :categories ], order: "posts.id").to_a + posts = Post.all.merge!(includes: [ :comments, :author, :categories ], order: Arel.sql("posts.id")).to_a assert_equal 2, posts.first.comments.size assert_equal 2, posts.first.categories.size assert_includes posts.first.comments, comments(:greetings) @@ -279,7 +279,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_loading_from_an_association - posts = authors(:david).posts.merge(includes: :comments, order: "posts.id").to_a + posts = authors(:david).posts.merge(includes: :comments, order: Arel.sql("posts.id")).to_a assert_equal 2, posts.first.comments.size end @@ -312,17 +312,17 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_nested_loading_through_has_one_association_with_order - aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "author_addresses.id").find(author_addresses(:david_address).id) + aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: Arel.sql("author_addresses.id")).find(author_addresses(:david_address).id) assert_equal aa.author.posts.count, aa.author.posts.length end def test_nested_loading_through_has_one_association_with_order_on_association - aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "authors.id").find(author_addresses(:david_address).id) + aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: Arel.sql("authors.id")).find(author_addresses(:david_address).id) assert_equal aa.author.posts.count, aa.author.posts.length end def test_nested_loading_through_has_one_association_with_order_on_nested_association - aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "posts.id").find(author_addresses(:david_address).id) + aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: Arel.sql("posts.id")).find(author_addresses(:david_address).id) assert_equal aa.author.posts.count, aa.author.posts.length end @@ -364,31 +364,31 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_association_loading_with_belongs_to_and_limit - comments = Comment.all.merge!(includes: :post, limit: 5, order: "comments.id").to_a + comments = Comment.all.merge!(includes: :post, limit: 5, order: Arel.sql("comments.id")).to_a assert_equal 5, comments.length assert_equal [1, 2, 3, 5, 6], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_conditions - comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, order: "comments.id").to_a + comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, order: Arel.sql("comments.id")).to_a assert_equal 3, comments.length assert_equal [5, 6, 7], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset - comments = Comment.all.merge!(includes: :post, limit: 3, offset: 2, order: "comments.id").to_a + comments = Comment.all.merge!(includes: :post, limit: 3, offset: 2, order: Arel.sql("comments.id")).to_a assert_equal 3, comments.length assert_equal [3, 5, 6], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions - comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, offset: 1, order: "comments.id").to_a + comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, offset: 1, order: Arel.sql("comments.id")).to_a assert_equal 3, comments.length assert_equal [6, 7, 8], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array - comments = Comment.all.merge!(includes: :post, where: ["post_id = ?", 4], limit: 3, offset: 1, order: "comments.id").to_a + comments = Comment.all.merge!(includes: :post, where: ["post_id = ?", 4], limit: 3, offset: 1, order: Arel.sql("comments.id")).to_a assert_equal 3, comments.length assert_equal [6, 7, 8], comments.collect(&:id) end @@ -402,7 +402,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_conditions_hash comments = [] assert_nothing_raised do - comments = Comment.all.merge!(includes: :post, where: { posts: { id: 4 } }, limit: 3, order: "comments.id").to_a + comments = Comment.all.merge!(includes: :post, where: { posts: { id: 4 } }, limit: 3, order: Arel.sql("comments.id")).to_a end assert_equal 3, comments.length assert_equal [5, 6, 7], comments.collect(&:id) @@ -420,25 +420,25 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_order_string_with_unquoted_table_name assert_nothing_raised do - Comment.all.merge!(includes: :post, order: "posts.id").to_a + Comment.all.merge!(includes: :post, order: Arel.sql("posts.id")).to_a end end def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id") assert_nothing_raised do - Comment.includes(:post).references(:posts).order(quoted_posts_id) + Comment.includes(:post).references(:posts).order(Arel.sql(quoted_posts_id)) end end def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations - posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, order: "posts.id").to_a + posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, order: Arel.sql("posts.id")).to_a assert_equal 1, posts.length assert_equal [1], posts.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations - posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, offset: 1, order: "posts.id").to_a + posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, offset: 1, order: Arel.sql("posts.id")).to_a assert_equal 1, posts.length assert_equal [2], posts.collect(&:id) end @@ -508,9 +508,9 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_through - posts_with_comments = people(:michael).posts.merge(includes: :comments, order: "posts.id").to_a - posts_with_author = people(:michael).posts.merge(includes: :author, order: "posts.id").to_a - posts_with_comments_and_author = people(:michael).posts.merge(includes: [ :comments, :author ], order: "posts.id").to_a + posts_with_comments = people(:michael).posts.merge(includes: :comments, order: Arel.sql("posts.id")).to_a + posts_with_author = people(:michael).posts.merge(includes: :author, order: Arel.sql("posts.id")).to_a + posts_with_comments_and_author = people(:michael).posts.merge(includes: [ :comments, :author ], order: Arel.sql("posts.id")).to_a assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum + post.comments.size } assert_equal authors(:david), assert_no_queries { posts_with_author.first.author } assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author } @@ -526,7 +526,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_through_an_sti_join_model - author = Author.all.merge!(includes: :special_post_comments, order: "authors.id").first + author = Author.all.merge!(includes: :special_post_comments, order: Arel.sql("authors.id")).first assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments } end @@ -539,14 +539,14 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both - author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: "authors.id").first + author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: Arel.sql("authors.id")).first assert_equal [], author.special_nonexistent_post_comments end def test_eager_with_has_many_through_join_model_with_conditions assert_equal Author.all.merge!(includes: :hello_post_comments, - order: "authors.id").first.hello_post_comments.sort_by(&:id), - Author.all.merge!(order: "authors.id").first.hello_post_comments.sort_by(&:id) + order: Arel.sql("authors.id")).first.hello_post_comments.sort_by(&:id), + Author.all.merge!(order: Arel.sql("authors.id")).first.hello_post_comments.sort_by(&:id) end def test_eager_with_has_many_through_join_model_with_conditions_on_top_level @@ -573,19 +573,19 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_and_limit - posts = Post.all.merge!(order: "posts.id asc", includes: [ :author, :comments ], limit: 2).to_a + posts = Post.all.merge!(order: Arel.sql("posts.id asc"), includes: [ :author, :comments ], limit: 2).to_a assert_equal 2, posts.size assert_equal 3, posts.inject(0) { |sum, post| sum + post.comments.size } end def test_eager_with_has_many_and_limit_and_conditions - posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.body = 'hello'", order: "posts.id").to_a + posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.body = 'hello'", order: Arel.sql("posts.id")).to_a assert_equal 2, posts.size assert_equal [4, 5], posts.collect(&:id) end def test_eager_with_has_many_and_limit_and_conditions_array - posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: [ "posts.body = ?", "hello" ], order: "posts.id").to_a + posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: [ "posts.body = ?", "hello" ], order: Arel.sql("posts.id")).to_a assert_equal 2, posts.size assert_equal [4, 5], posts.collect(&:id) end @@ -643,7 +643,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_and_belongs_to_many_and_limit - posts = Post.all.merge!(includes: :categories, order: "posts.id", limit: 3).to_a + posts = Post.all.merge!(includes: :categories, order: Arel.sql("posts.id"), limit: 3).to_a assert_equal 3, posts.size assert_equal 2, posts[0].categories.size assert_equal 1, posts[1].categories.size @@ -709,7 +709,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_association_loading_with_habtm - posts = Post.all.merge!(includes: :categories, order: "posts.id").to_a + posts = Post.all.merge!(includes: :categories, order: Arel.sql("posts.id")).to_a assert_equal 2, posts[0].categories.size assert_equal 1, posts[1].categories.size assert_equal 0, posts[2].categories.size @@ -874,14 +874,14 @@ class EagerAssociationTest < ActiveRecord::TestCase posts(:thinking, :sti_comments), Post.all.merge!( includes: [:author, :comments], where: { "authors.name" => "David" }, - order: "UPPER(posts.title)", limit: 2, offset: 1 + order: Arel.sql("UPPER(posts.title)"), limit: 2, offset: 1 ).to_a ) assert_equal( posts(:sti_post_and_comments, :sti_comments), Post.all.merge!( includes: [:author, :comments], where: { "authors.name" => "David" }, - order: "UPPER(posts.title) DESC", limit: 2, offset: 1 + order: Arel.sql("UPPER(posts.title) DESC"), limit: 2, offset: 1 ).to_a ) end @@ -891,14 +891,14 @@ class EagerAssociationTest < ActiveRecord::TestCase posts(:thinking, :sti_comments), Post.all.merge!( includes: [:author, :comments], where: { "authors.name" => "David" }, - order: ["UPPER(posts.title)", "posts.id"], limit: 2, offset: 1 + order: [Arel.sql("UPPER(posts.title)"), Arel.sql("posts.id")], limit: 2, offset: 1 ).to_a ) assert_equal( posts(:sti_post_and_comments, :sti_comments), Post.all.merge!( includes: [:author, :comments], where: { "authors.name" => "David" }, - order: ["UPPER(posts.title) DESC", "posts.id"], limit: 2, offset: 1 + order: [Arel.sql("UPPER(posts.title) DESC"), Arel.sql("posts.id")], limit: 2, offset: 1 ).to_a ) end @@ -909,7 +909,7 @@ class EagerAssociationTest < ActiveRecord::TestCase Person.references(:number1_fans_people).merge( includes: [:readers, :primary_contact, :number1_fan], where: "number1_fans_people.first_name like 'M%'", - order: "people.id", limit: 2, offset: 0 + order: Arel.sql("people.id"), limit: 2, offset: 0 ).to_a ) end @@ -1089,12 +1089,12 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_order_on_join_table_with_include_and_limit - assert_equal 5, Developer.all.merge!(includes: "projects", order: "developers_projects.joined_on DESC", limit: 5).to_a.size + assert_equal 5, Developer.all.merge!(includes: "projects", order: Arel.sql("developers_projects.joined_on DESC"), limit: 5).to_a.size end def test_eager_loading_with_order_on_joined_table_preloads posts = assert_queries(2) do - Post.all.merge!(joins: :comments, includes: :author, order: "comments.id DESC").to_a + Post.all.merge!(joins: :comments, includes: :author, order: Arel.sql("comments.id DESC")).to_a end assert_equal posts(:eager_other), posts[1] assert_equal authors(:mary), assert_no_queries { posts[1].author } @@ -1102,18 +1102,18 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_loading_with_conditions_on_joined_table_preloads posts = assert_queries(2) do - Post.all.merge!(select: "distinct posts.*", includes: :author, joins: [:comments], where: "comments.body like 'Thank you%'", order: "posts.id").to_a + Post.all.merge!(select: "distinct posts.*", includes: :author, joins: [:comments], where: "comments.body like 'Thank you%'", order: Arel.sql("posts.id")).to_a end assert_equal [posts(:welcome)], posts assert_equal authors(:david), assert_no_queries { posts[0].author } posts = assert_queries(2) do - Post.all.merge!(includes: :author, joins: { taggings: :tag }, where: "tags.name = 'General'", order: "posts.id").to_a + Post.all.merge!(includes: :author, joins: { taggings: :tag }, where: "tags.name = 'General'", order: Arel.sql("posts.id")).to_a end assert_equal posts(:welcome, :thinking), posts posts = assert_queries(2) do - Post.all.merge!(includes: :author, joins: { taggings: { tag: :taggings } }, where: "taggings_tags.super_tag_id=2", order: "posts.id").to_a + Post.all.merge!(includes: :author, joins: { taggings: { tag: :taggings } }, where: "taggings_tags.super_tag_id=2", order: Arel.sql("posts.id")).to_a end assert_equal posts(:welcome, :thinking), posts end @@ -1132,13 +1132,13 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_loading_with_conditions_on_string_joined_table_preloads posts = assert_queries(2) do - Post.all.merge!(select: "distinct posts.*", includes: :author, joins: "INNER JOIN comments on comments.post_id = posts.id", where: "comments.body like 'Thank you%'", order: "posts.id").to_a + Post.all.merge!(select: "distinct posts.*", includes: :author, joins: "INNER JOIN comments on comments.post_id = posts.id", where: "comments.body like 'Thank you%'", order: Arel.sql("posts.id")).to_a end assert_equal [posts(:welcome)], posts assert_equal authors(:david), assert_no_queries { posts[0].author } posts = assert_queries(2) do - Post.all.merge!(select: "distinct posts.*", includes: :author, joins: ["INNER JOIN comments on comments.post_id = posts.id"], where: "comments.body like 'Thank you%'", order: "posts.id").to_a + Post.all.merge!(select: "distinct posts.*", includes: :author, joins: ["INNER JOIN comments on comments.post_id = posts.id"], where: "comments.body like 'Thank you%'", order: Arel.sql("posts.id")).to_a end assert_equal [posts(:welcome)], posts assert_equal authors(:david), assert_no_queries { posts[0].author } @@ -1146,7 +1146,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_loading_with_select_on_joined_table_preloads posts = assert_queries(2) do - Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: "posts.id").to_a + Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: Arel.sql("posts.id")).to_a end assert_equal "David", posts[0].author_name assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments } @@ -1190,7 +1190,7 @@ class EagerAssociationTest < ActiveRecord::TestCase if current_adapter?(:OracleAdapter) firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies"[0, 30] + ".name").find(1) else - firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies.name").find(1) + firm = Firm.all.merge!(includes: :clients_using_primary_key, order: Arel.sql("clients_using_primary_keys_companies.name")).find(1) end assert_no_queries do assert_equal expected, firm.clients_using_primary_key @@ -1199,7 +1199,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_preload_has_one_using_primary_key expected = accounts(:signals37) - firm = Firm.all.merge!(includes: :account_using_primary_key, order: "companies.id").first + firm = Firm.all.merge!(includes: :account_using_primary_key, order: Arel.sql("companies.id")).first assert_no_queries do assert_equal expected, firm.account_using_primary_key end @@ -1207,7 +1207,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_include_has_one_using_primary_key expected = accounts(:signals37) - firm = Firm.all.merge!(includes: :account_using_primary_key, order: "accounts.id").to_a.detect { |f| f.id == 1 } + firm = Firm.all.merge!(includes: :account_using_primary_key, order: Arel.sql("accounts.id")).to_a.detect { |f| f.id == 1 } assert_no_queries do assert_equal expected, firm.account_using_primary_key end @@ -1269,7 +1269,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_joins_with_includes_should_preload_via_joins - post = assert_queries(1) { Post.includes(:comments).joins(:comments).order("posts.id desc").to_a.first } + post = assert_queries(1) { Post.includes(:comments).joins(:comments).order(Arel.sql("posts.id desc")).to_a.first } assert_queries(0) do assert_not_equal 0, post.comments.to_a.count @@ -1278,16 +1278,16 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_join_eager_with_empty_order_should_generate_valid_sql assert_nothing_raised do - Post.includes(:comments).order("").where(comments: { body: "Thank you for the welcome" }).first + Post.includes(:comments).order(Arel.sql("")).where(comments: { body: "Thank you for the welcome" }).first end end def test_deep_including_through_habtm # warm up habtm cache - posts = Post.all.merge!(includes: { categories: :categorizations }, order: "posts.id").to_a + posts = Post.all.merge!(includes: { categories: :categorizations }, order: Arel.sql("posts.id")).to_a posts[0].categories[0].categorizations.length - posts = Post.all.merge!(includes: { categories: :categorizations }, order: "posts.id").to_a + posts = Post.all.merge!(includes: { categories: :categorizations }, order: Arel.sql("posts.id")).to_a assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length } assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length } assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length } @@ -1382,7 +1382,7 @@ class EagerAssociationTest < ActiveRecord::TestCase test "preloading associations with string joins and order references" do author = assert_queries(2) { - Author.includes(:posts).joins("LEFT JOIN posts ON posts.author_id = authors.id").order("posts.title DESC").first + Author.includes(:posts).joins("LEFT JOIN posts ON posts.author_id = authors.id").order(Arel.sql("posts.title DESC")).first } assert_no_queries { assert_equal 5, author.posts.size @@ -1513,6 +1513,6 @@ class EagerAssociationTest < ActiveRecord::TestCase private def find_all_ordered(klass, include = nil) - klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a + klass.order(Arel.sql("#{klass.table_name}.#{klass.primary_key}")).includes(include).to_a 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 c817d7267b..7e5655bb08 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 @@ -587,7 +587,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase end def test_find_should_append_to_association_order - ordered_developers = projects(:active_record).developers.order("projects.id") + ordered_developers = projects(:active_record).developers.order(Arel.sql("projects.id")) assert_equal ["developers.name desc, developers.id desc", "projects.id"], ordered_developers.order_values end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 6bd11a5d81..85733f9056 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -48,7 +48,7 @@ class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCa author = authors(:david) # this can fail on adapters which require ORDER BY expressions to be included in the SELECT expression # if the reorder clauses are not correctly handled - assert author.posts_with_comments_sorted_by_comment_id.where("comments.id > 0").reorder("posts.comments_count DESC", "posts.tags_count DESC").last + assert author.posts_with_comments_sorted_by_comment_id.where("comments.id > 0").reorder(Arel.sql("posts.comments_count DESC"), Arel.sql("posts.tags_count DESC")).last end end @@ -550,7 +550,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_find_should_append_to_association_order - ordered_clients = companies(:first_firm).clients_sorted_desc.order("companies.id") + ordered_clients = companies(:first_firm).clients_sorted_desc.order(Arel.sql("companies.id")) assert_equal ["id DESC", "companies.id"], ordered_clients.order_values end @@ -1123,7 +1123,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_deleting_updates_counter_cache - topic = Topic.order("id ASC").first + topic = Topic.order(Arel.sql("id ASC")).first assert_equal topic.replies.to_a.size, topic.replies_count topic.replies.delete(topic.replies.first) @@ -1162,7 +1162,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_pushing_association_updates_counter_cache - topic = Topic.order("id ASC").first + topic = Topic.order(Arel.sql("id ASC")).first reply = Reply.create! assert_difference "topic.reload.replies_count", 1 do @@ -1212,7 +1212,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_calling_update_attributes_on_id_changes_the_counter_cache - topic = Topic.order("id ASC").first + topic = Topic.order(Arel.sql("id ASC")).first original_count = topic.replies.to_a.size assert_equal original_count, topic.replies_count 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 046020e310..ed96eb54c1 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -71,7 +71,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def self.name; "Person"; end has_many :readers - has_many :posts, -> { order("posts.id DESC") }, through: :readers + has_many :posts, -> { order(Arel.sql("posts.id DESC")) }, through: :readers end posts = person_prime.includes(:posts).first.posts @@ -985,7 +985,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_joining_has_many_through_belongs_to - posts = Post.joins(:author_categorizations).order("posts.id"). + posts = Post.joins(:author_categorizations).order(Arel.sql("posts.id")). where("categorizations.id" => categorizations(:mary_thinking_sti).id) assert_equal [posts(:eager_other), posts(:misc_by_mary), posts(:other_by_mary)], posts 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 fe24c465b2..bef20b2ebe 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -139,7 +139,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_nonpreload_eagerloading members = assert_queries(1) do - Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback + Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: Arel.sql("clubs.name")).to_a #force fallback end assert_equal 1, members.size assert_not_nil assert_no_queries { members[0].club } @@ -147,7 +147,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_nonpreload_eager_loading_through_polymorphic members = assert_queries(1) do - Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback + Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: Arel.sql("clubs.name")).to_a #force fallback end assert_equal 1, members.size assert_not_nil assert_no_queries { members[0].sponsor_club } @@ -156,7 +156,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record Sponsor.new(sponsor_club: clubs(:crazy_club), sponsorable: members(:groucho)).save! members = assert_queries(1) do - Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name DESC").to_a #force fallback + Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: Arel.sql("clubs.name DESC")).to_a #force fallback end assert_equal 1, members.size assert_not_nil assert_no_queries { members[0].sponsor_club } diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index e13cf93dcf..c39e967569 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -223,7 +223,7 @@ class InverseHasOneTests < ActiveRecord::TestCase f.man.name = "Mungo" assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance" - m = Man.all.merge!(where: { name: "Gordon" }, includes: :face, order: "faces.id").first + m = Man.all.merge!(where: { name: "Gordon" }, includes: :face, order: Arel.sql("faces.id")).first f = m.face assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance" m.name = "Bongo" @@ -329,7 +329,7 @@ class InverseHasManyTests < ActiveRecord::TestCase assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance" end - m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests, order: "interests.id").first + m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests, order: Arel.sql("interests.id")).first is = m.interests is.each do |i| assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance" @@ -554,7 +554,7 @@ class InverseBelongsToTests < ActiveRecord::TestCase m.face.description = "pleasing" assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance" - f = Face.all.merge!(includes: :man, order: "men.id", where: { description: "trusting" }).first + f = Face.all.merge!(includes: :man, order: Arel.sql("men.id"), where: { description: "trusting" }).first m = f.man assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance" f.description = "gormless" @@ -637,7 +637,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase m.polymorphic_face.description = "pleasing" assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance" - f = Face.all.merge!(where: { description: "confused" }, includes: :man, order: "men.id").first + f = Face.all.merge!(where: { description: "confused" }, includes: :man, order: Arel.sql("men.id")).first m = f.polymorphic_man assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance" f.description = "gormless" diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 87694b0788..8f08684820 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -244,8 +244,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_include_has_many_through - posts = Post.all.merge!(order: "posts.id").to_a - posts_with_authors = Post.all.merge!(includes: :authors, order: "posts.id").to_a + posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a + posts_with_authors = Post.all.merge!(includes: :authors, order: Arel.sql("posts.id")).to_a assert_equal posts.length, posts_with_authors.length posts.length.times do |i| assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length } @@ -269,8 +269,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_include_polymorphic_has_many_through - posts = Post.all.merge!(order: "posts.id").to_a - posts_with_tags = Post.all.merge!(includes: :tags, order: "posts.id").to_a + posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a + posts_with_tags = Post.all.merge!(includes: :tags, order: Arel.sql("posts.id")).to_a assert_equal posts.length, posts_with_tags.length posts.length.times do |i| assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length } @@ -278,8 +278,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_include_polymorphic_has_many - posts = Post.all.merge!(order: "posts.id").to_a - posts_with_taggings = Post.all.merge!(includes: :taggings, order: "posts.id").to_a + posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a + posts_with_taggings = Post.all.merge!(includes: :taggings, order: Arel.sql("posts.id")).to_a assert_equal posts.length, posts_with_taggings.length posts.length.times do |i| assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length } @@ -326,7 +326,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_through_with_custom_primary_key_on_has_many_source - assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order("authors.id") + assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order(Arel.sql("authors.id")) end def test_belongs_to_polymorphic_with_counter_cache @@ -383,19 +383,19 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_through_has_many_find_all - assert_equal comments(:greetings), authors(:david).comments.order("comments.id").to_a.first + assert_equal comments(:greetings), authors(:david).comments.order(Arel.sql("comments.id")).to_a.first end def test_has_many_through_has_many_find_all_with_custom_class - assert_equal comments(:greetings), authors(:david).funky_comments.order("comments.id").to_a.first + assert_equal comments(:greetings), authors(:david).funky_comments.order(Arel.sql("comments.id")).to_a.first end def test_has_many_through_has_many_find_first - assert_equal comments(:greetings), authors(:david).comments.order("comments.id").first + assert_equal comments(:greetings), authors(:david).comments.order(Arel.sql("comments.id")).first end def test_has_many_through_has_many_find_conditions - options = { where: "comments.#{QUOTED_TYPE}='SpecialComment'", order: "comments.id" } + options = { where: "comments.#{QUOTED_TYPE}='SpecialComment'", order: Arel.sql("comments.id") } assert_equal comments(:does_it_hurt), authors(:david).comments.merge(options).first end @@ -420,7 +420,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_eager_load_has_many_through_has_many - author = Author.all.merge!(where: ["name = ?", "David"], includes: :comments, order: "comments.id").first + author = Author.all.merge!(where: ["name = ?", "David"], includes: :comments, order: Arel.sql("comments.id")).first SpecialComment.new; VerySpecialComment.new assert_no_queries do assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], author.comments.collect(&:id) @@ -661,8 +661,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_preload_polymorphic_has_many_through - posts = Post.all.merge!(order: "posts.id").to_a - posts_with_tags = Post.all.merge!(includes: :tags, order: "posts.id").to_a + posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a + posts_with_tags = Post.all.merge!(includes: :tags, order: Arel.sql("posts.id")).to_a assert_equal posts.length, posts_with_tags.length posts.length.times do |i| assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length } @@ -688,8 +688,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_preload_polymorphic_has_many - posts = Post.all.merge!(order: "posts.id").to_a - posts_with_taggings = Post.all.merge!(includes: :taggings, order: "posts.id").to_a + posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a + posts_with_taggings = Post.all.merge!(includes: :taggings, order: Arel.sql("posts.id")).to_a assert_equal posts.length, posts_with_taggings.length posts.length.times do |i| assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length } diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 65d30d011b..b8e8f48ae5 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -86,7 +86,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase # Through: has_many through def test_has_many_through_has_many_through_with_has_many_source_reflection luke, david = subscribers(:first), subscribers(:second) - assert_equal [luke, david, david], authors(:david).subscribers.order("subscribers.nick") + assert_equal [luke, david, david], authors(:david).subscribers.order(Arel.sql("subscribers.nick")) end def test_has_many_through_has_many_through_with_has_many_source_reflection_preload @@ -156,7 +156,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) assert_equal [groucho_details, other_details], - members(:groucho).organization_member_details.order("member_details.id") + members(:groucho).organization_member_details.order(Arel.sql("member_details.id")) end def test_has_many_through_has_one_with_has_many_through_source_reflection_preload @@ -171,7 +171,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_one_with_has_many_through_source_reflection_preload_via_joins assert_includes_and_joins_equal( - Member.where("member_details.id" => member_details(:groucho).id).order("member_details.id"), + Member.where("member_details.id" => member_details(:groucho).id).order(Arel.sql("member_details.id")), [members(:groucho), members(:some_other_guy)], :organization_member_details ) @@ -187,7 +187,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) assert_equal [groucho_details, other_details], - members(:groucho).organization_member_details_2.order("member_details.id") + members(:groucho).organization_member_details_2.order(Arel.sql("member_details.id")) end def test_has_many_through_has_one_through_with_has_many_source_reflection_preload @@ -203,7 +203,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_one_through_with_has_many_source_reflection_preload_via_joins assert_includes_and_joins_equal( - Member.where("member_details.id" => member_details(:groucho).id).order("member_details.id"), + Member.where("member_details.id" => member_details(:groucho).id).order(Arel.sql("member_details.id")), [members(:groucho), members(:some_other_guy)], :organization_member_details_2 ) @@ -218,7 +218,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection general, cooking = categories(:general), categories(:cooking) - assert_equal [general, cooking], authors(:bob).post_categories.order("categories.id") + assert_equal [general, cooking], authors(:bob).post_categories.order(Arel.sql("categories.id")) end def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload @@ -246,7 +246,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection greetings, more = comments(:greetings), comments(:more_greetings) - assert_equal [greetings, more], categories(:technology).post_comments.order("comments.id") + assert_equal [greetings, more], categories(:technology).post_comments.order(Arel.sql("comments.id")) end def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload @@ -264,7 +264,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase Category.joins(:post_comments).first assert_includes_and_joins_equal( - Category.where("comments.id" => comments(:more_greetings).id).order("categories.id"), + Category.where("comments.id" => comments(:more_greetings).id).order(Arel.sql("categories.id")), [categories(:general), categories(:technology)], :post_comments ) end @@ -275,7 +275,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection greetings, more = comments(:greetings), comments(:more_greetings) - assert_equal [greetings, more], authors(:bob).category_post_comments.order("comments.id") + assert_equal [greetings, more], authors(:bob).category_post_comments.order(Arel.sql("comments.id")) end def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload @@ -292,7 +292,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase Author.joins(:category_post_comments).first assert_includes_and_joins_equal( - Author.where("comments.id" => comments(:does_it_hurt).id).order("authors.id"), + Author.where("comments.id" => comments(:does_it_hurt).id).order(Arel.sql("authors.id")), [authors(:david), authors(:mary)], :category_post_comments ) end @@ -327,7 +327,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase welcome_general, thinking_general = taggings(:welcome_general), taggings(:thinking_general) assert_equal [welcome_general, thinking_general], - categorizations(:david_welcome_general).post_taggings.order("taggings.id") + categorizations(:david_welcome_general).post_taggings.order(Arel.sql("taggings.id")) end def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload @@ -341,7 +341,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload_via_joins assert_includes_and_joins_equal( - Categorization.where("taggings.id" => taggings(:welcome_general).id).order("taggings.id"), + Categorization.where("taggings.id" => taggings(:welcome_general).id).order(Arel.sql("taggings.id")), [categorizations(:david_welcome_general)], :post_taggings ) end @@ -411,7 +411,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_distinct_has_many_through_a_has_many_through_association_on_through_reflection author = authors(:david) assert_equal [subscribers(:first), subscribers(:second)], - author.distinct_subscribers.order("subscribers.nick") + author.distinct_subscribers.order(Arel.sql("subscribers.nick")) end def test_nested_has_many_through_with_a_table_referenced_multiple_times @@ -436,7 +436,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase end def test_has_many_through_with_foreign_key_option_on_through_reflection - assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order("posts.id") + assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order(Arel.sql("posts.id")) assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors references = Reference.joins(:agents_posts_authors).where("authors.id" => authors(:david).id) @@ -444,7 +444,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase end def test_has_many_through_with_foreign_key_option_on_source_reflection - assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order("people.id") + assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order(Arel.sql("people.id")) jobs = Job.joins(:agents) assert_equal [jobs(:unicyclist), jobs(:unicyclist)], jobs diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index f0ef522515..e2e4aa22f4 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -439,7 +439,7 @@ class BasicsTest < ActiveRecord::TestCase if current_adapter?(:Mysql2Adapter) def test_update_all_with_order_and_limit - assert_equal 1, Topic.limit(1).order("id DESC").update_all(content: "bulk updated!") + assert_equal 1, Topic.limit(1).order(Arel.sql("id DESC")).update_all(content: "bulk updated!") end end @@ -1081,11 +1081,11 @@ class BasicsTest < ActiveRecord::TestCase def test_find_last last = Developer.last - assert_equal last, Developer.all.merge!(order: "id desc").first + assert_equal last, Developer.all.merge!(order: Arel.sql("id desc")).first end def test_last - assert_equal Developer.all.merge!(order: "id desc").first, Developer.last + assert_equal Developer.all.merge!(order: Arel.sql("id desc")).first, Developer.last end def test_all @@ -1095,27 +1095,27 @@ class BasicsTest < ActiveRecord::TestCase end def test_all_with_conditions - assert_equal Developer.all.merge!(order: "id desc").to_a, Developer.order("id desc").to_a + assert_equal Developer.all.merge!(order: Arel.sql("id desc")).to_a, Developer.order(Arel.sql("id desc")).to_a end def test_find_ordered_last - last = Developer.all.merge!(order: "developers.salary ASC").last - assert_equal last, Developer.all.merge!(order: "developers.salary ASC").to_a.last + last = Developer.all.merge!(order: Arel.sql("developers.salary ASC")).last + assert_equal last, Developer.all.merge!(order: Arel.sql("developers.salary ASC")).to_a.last end def test_find_reverse_ordered_last - last = Developer.all.merge!(order: "developers.salary DESC").last - assert_equal last, Developer.all.merge!(order: "developers.salary DESC").to_a.last + last = Developer.all.merge!(order: Arel.sql("developers.salary DESC")).last + assert_equal last, Developer.all.merge!(order: Arel.sql("developers.salary DESC")).to_a.last end def test_find_multiple_ordered_last - last = Developer.all.merge!(order: "developers.name, developers.salary DESC").last - assert_equal last, Developer.all.merge!(order: "developers.name, developers.salary DESC").to_a.last + last = Developer.all.merge!(order: Arel.sql("developers.name, developers.salary DESC")).last + assert_equal last, Developer.all.merge!(order: Arel.sql("developers.name, developers.salary DESC")).to_a.last end def test_find_keeps_multiple_order_values - combined = Developer.all.merge!(order: "developers.name, developers.salary").to_a - assert_equal combined, Developer.all.merge!(order: ["developers.name", "developers.salary"]).to_a + combined = Developer.all.merge!(order: Arel.sql("developers.name, developers.salary")).to_a + assert_equal combined, Developer.all.merge!(order: [Arel.sql("developers.name"), Arel.sql("developers.salary")]).to_a end def test_find_keeps_multiple_group_values diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index be8aeed5ac..ff345d5f0e 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -9,7 +9,7 @@ class EachTest < ActiveRecord::TestCase fixtures :posts, :subscribers def setup - @posts = Post.order("id asc") + @posts = Post.order(Arel.sql("id asc")) @total = Post.count Post.count("id") # preheat arel's table cache end @@ -101,7 +101,7 @@ class EachTest < ActiveRecord::TestCase previous_logger = ActiveRecord::Base.logger ActiveRecord::Base.logger = nil assert_nothing_raised do - Post.order("comments_count DESC").find_each { |post| post } + Post.order(Arel.sql("comments_count DESC")).find_each { |post| post } end ensure ActiveRecord::Base.logger = previous_logger @@ -233,7 +233,7 @@ class EachTest < ActiveRecord::TestCase end def test_find_in_batches_should_use_any_column_as_primary_key - nick_order_subscribers = Subscriber.order("nick asc") + nick_order_subscribers = Subscriber.order(Arel.sql("nick asc")) start_nick = nick_order_subscribers.second.nick subscribers = [] @@ -329,7 +329,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_each_record_should_be_ordered_by_id - ids = Post.order("id ASC").pluck(:id) + ids = Post.order(Arel.sql("id ASC")).pluck(:id) assert_queries(6) do Post.in_batches(of: 2).each_record.with_index do |post, i| assert_equal ids[i], post.id @@ -384,7 +384,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_should_start_from_the_start_option - post = Post.order("id ASC").where("id >= ?", 2).first + post = Post.order(Arel.sql("id ASC")).where("id >= ?", 2).first assert_queries(2) do relation = Post.in_batches(of: 1, start: 2).first assert_equal post, relation.first @@ -392,7 +392,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_should_end_at_the_finish_option - post = Post.order("id DESC").where("id <= ?", 5).first + post = Post.order(Arel.sql("id DESC")).where("id <= ?", 5).first assert_queries(7) do relation = Post.in_batches(of: 1, finish: 5, load: true).reverse_each.first assert_equal post, relation.last @@ -451,7 +451,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_should_use_any_column_as_primary_key - nick_order_subscribers = Subscriber.order("nick asc") + nick_order_subscribers = Subscriber.order(Arel.sql("nick asc")) start_nick = nick_order_subscribers.second.nick subscribers = [] diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index d8bc917e7f..65ebfcd989 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -239,19 +239,19 @@ class FinderTest < ActiveRecord::TestCase # Ensure +exists?+ runs without an error by excluding order value. def test_exists_with_order - assert_equal true, Topic.order("invalid sql here").exists? + assert_equal true, Topic.order(Arel.sql("invalid sql here")).exists? end def test_exists_with_joins - assert_equal true, Topic.joins(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists? + assert_equal true, Topic.joins(:replies).where(replies_topics: { approved: true }).order(Arel.sql("replies_topics.created_at DESC")).exists? end def test_exists_with_left_joins - assert_equal true, Topic.left_joins(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists? + assert_equal true, Topic.left_joins(:replies).where(replies_topics: { approved: true }).order(Arel.sql("replies_topics.created_at DESC")).exists? end def test_exists_with_eager_load - assert_equal true, Topic.eager_load(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists? + assert_equal true, Topic.eager_load(:replies).where(replies_topics: { approved: true }).order(Arel.sql("replies_topics.created_at DESC")).exists? end def test_exists_with_includes_limit_and_empty_result @@ -267,8 +267,8 @@ class FinderTest < ActiveRecord::TestCase def test_exists_with_distinct_association_includes_limit_and_order author = Author.first - assert_equal false, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(0).exists? - assert_equal true, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(1).exists? + assert_equal false, author.unique_categorized_posts.includes(:special_comments).order(Arel.sql("comments.tags_count DESC")).limit(0).exists? + assert_equal true, author.unique_categorized_posts.includes(:special_comments).order(Arel.sql("comments.tags_count DESC")).limit(1).exists? end def test_exists_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association @@ -652,7 +652,7 @@ class FinderTest < ActiveRecord::TestCase def test_last_with_irreversible_order assert_raises(ActiveRecord::IrreversibleOrderError) do - Topic.order("coalesce(author_name, title)").last + Topic.order(Arel.sql("coalesce(author_name, title)")).last end end @@ -813,9 +813,9 @@ class FinderTest < ActiveRecord::TestCase end def test_hash_condition_find_with_array - p1, p2 = Post.limit(2).order("id asc").to_a - assert_equal [p1, p2], Post.where(id: [p1, p2]).order("id asc").to_a - assert_equal [p1, p2], Post.where(id: [p1, p2.id]).order("id asc").to_a + p1, p2 = Post.limit(2).order(Arel.sql("id asc")).to_a + assert_equal [p1, p2], Post.where(id: [p1, p2]).order(Arel.sql("id asc")).to_a + assert_equal [p1, p2], Post.where(id: [p1, p2.id]).order(Arel.sql("id asc")).to_a end def test_hash_condition_find_with_nil @@ -1013,7 +1013,7 @@ class FinderTest < ActiveRecord::TestCase end def test_find_by_one_attribute_with_several_options - assert_equal accounts(:unknown), Account.order("id DESC").where("id != ?", 3).find_by_credit_limit(50) + assert_equal accounts(:unknown), Account.order(Arel.sql("id DESC")).where("id != ?", 3).find_by_credit_limit(50) end def test_find_by_one_missing_attribute @@ -1041,7 +1041,7 @@ class FinderTest < ActiveRecord::TestCase assert_equal devs[2], Developer.offset(2).first assert_equal devs[-3], Developer.offset(2).last - assert_equal devs[-3], Developer.offset(2).order("id DESC").first + assert_equal devs[-3], Developer.offset(2).order(Arel.sql("id DESC")).first end def test_find_by_nil_attribute @@ -1094,9 +1094,9 @@ class FinderTest < ActiveRecord::TestCase end def test_find_by_records - p1, p2 = Post.limit(2).order("id asc").to_a - assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2]]).order("id asc") - assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2.id]]).order("id asc") + p1, p2 = Post.limit(2).order(Arel.sql("id asc")).to_a + assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2]]).order(Arel.sql("id asc")) + assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2.id]]).order(Arel.sql("id asc")) end def test_select_value @@ -1125,18 +1125,18 @@ class FinderTest < ActiveRecord::TestCase def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct assert_equal 2, Post.includes(authors: :author_address). where.not(author_addresses: { id: nil }). - order("author_addresses.id DESC").limit(2).to_a.size + order(Arel.sql("author_addresses.id DESC")).limit(2).to_a.size assert_equal 3, Post.includes(author: :author_address, authors: :author_address). where.not(author_addresses_authors: { id: nil }). - order("author_addresses_authors.id DESC").limit(3).to_a.size + order(Arel.sql("author_addresses_authors.id DESC")).limit(3).to_a.size end def test_find_with_nil_inside_set_passed_for_one_attribute client_of = Company. where(client_of: [2, 1, nil], name: ["37signals", "Summit", "Microsoft"]). - order("client_of DESC"). + order(Arel.sql("client_of DESC")). map(&:client_of) assert_includes client_of, nil @@ -1146,7 +1146,7 @@ class FinderTest < ActiveRecord::TestCase def test_find_with_nil_inside_set_passed_for_attribute client_of = Company. where(client_of: [nil]). - order("client_of DESC"). + order(Arel.sql("client_of DESC")). map(&:client_of) assert_equal [], client_of.compact @@ -1155,7 +1155,7 @@ class FinderTest < ActiveRecord::TestCase def test_with_limiting_with_custom_select posts = Post.references(:authors).merge( includes: :author, select: 'posts.*, authors.id as "author_id"', - limit: 3, order: "posts.id" + limit: 3, order: Arel.sql("posts.id") ).to_a assert_equal 3, posts.size assert_equal [0, 1, 1], posts.map(&:author_id).sort diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 953e0fee76..3b35df526f 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -13,10 +13,10 @@ class RelationMergingTest < ActiveRecord::TestCase fixtures :developers, :comments, :authors, :author_addresses, :posts def test_relation_merging - devs = Developer.where("salary >= 80000").merge(Developer.limit(2)).merge(Developer.order("id ASC").where("id < 3")) + devs = Developer.where("salary >= 80000").merge(Developer.limit(2)).merge(Developer.order(Arel.sql("id ASC")).where("id < 3")) assert_equal [developers(:david), developers(:jamis)], devs.to_a - dev_with_count = Developer.limit(1).merge(Developer.order("id DESC")).merge(Developer.select("developers.*")) + dev_with_count = Developer.limit(1).merge(Developer.order(Arel.sql("id DESC"))).merge(Developer.select("developers.*")) assert_equal [developers(:poor_jamis)], dev_with_count.to_a end @@ -47,8 +47,8 @@ class RelationMergingTest < ActiveRecord::TestCase def test_relation_merging_with_eager_load relations = [] - relations << Post.order("comments.id DESC").merge(Post.eager_load(:last_comment)).merge(Post.all) - relations << Post.eager_load(:last_comment).merge(Post.order("comments.id DESC")).merge(Post.all) + relations << Post.order(Arel.sql("comments.id DESC")).merge(Post.eager_load(:last_comment)).merge(Post.all) + relations << Post.eager_load(:last_comment).merge(Post.order(Arel.sql("comments.id DESC"))).merge(Post.all) relations.each do |posts| post = posts.find { |p| p.id == 1 } @@ -57,7 +57,7 @@ class RelationMergingTest < ActiveRecord::TestCase end def test_relation_merging_with_locks - devs = Developer.lock.where("salary >= 80000").order("id DESC").merge(Developer.limit(2)) + devs = Developer.lock.where("salary >= 80000").order(Arel.sql("id DESC")).merge(Developer.limit(2)) assert devs.locked? end @@ -118,7 +118,7 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase test "merging where relations" do hello_by_bob = Post.where(body: "hello").joins(:author). - merge(Author.where(name: "Bob")).order("posts.id").pluck(Arel.sql("posts.id")) + merge(Author.where(name: "Bob")).order(Arel.sql("posts.id")).pluck(Arel.sql("posts.id")) assert_equal [posts(:misc_by_bob).id, posts(:other_by_bob).id], hello_by_bob @@ -131,7 +131,7 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase assert_equal ["Bob", "Bob", "David"], posts_by_author_name posts_by_author_name = Post.limit(3).joins(:author). - merge(Author.order("name")).pluck(Arel.sql("authors.name")) + merge(Author.order(Arel.sql("name"))).pluck(Arel.sql("authors.name")) assert_equal ["Bob", "Bob", "David"], posts_by_author_name end diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb index ad3700b73a..d845c07a50 100644 --- a/activerecord/test/cases/relation/mutation_test.rb +++ b/activerecord/test/cases/relation/mutation_test.rb @@ -94,7 +94,7 @@ module ActiveRecord end test "reverse_order!" do - @relation = Post.order("title ASC, comments_count DESC") + @relation = Post.order(Arel.sql("title ASC, comments_count DESC")) relation.reverse_order! diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb index 7e418f9c7d..2abc3b7fe8 100644 --- a/activerecord/test/cases/relation/or_test.rb +++ b/activerecord/test/cases/relation/or_test.rb @@ -50,15 +50,15 @@ module ActiveRecord end def test_or_preserves_other_querying_methods - expected = Post.where("id = 1 or id = 2 or id = 3").order("body asc").to_a - partial = Post.order("body asc") + expected = Post.where("id = 1 or id = 2 or id = 3").order(Arel.sql("body asc")).to_a + partial = Post.order(Arel.sql("body asc")) assert_equal expected, partial.where("id = 1").or(partial.where(id: [2, 3])).to_a - assert_equal expected, Post.order("body asc").where("id = 1").or(Post.order("body asc").where(id: [2, 3])).to_a + assert_equal expected, Post.order(Arel.sql("body asc")).where("id = 1").or(Post.order(Arel.sql("body asc")).where(id: [2, 3])).to_a end def test_or_with_incompatible_relations error = assert_raises ArgumentError do - Post.order("body asc").where("id = 1").or(Post.order("id desc").where(id: [2, 3])).to_a + Post.order(Arel.sql("body asc")).where("id = 1").or(Post.order(Arel.sql("id desc")).where(id: [2, 3])).to_a end assert_equal "Relation passed to #or must be structurally compatible. Incompatible values: [:order]", error.message @@ -78,12 +78,12 @@ module ActiveRecord def test_or_with_unscope_order expected = Post.where("id = 1 or id = 2") - assert_equal expected, Post.order("body asc").where("id = 1").unscope(:order).or(Post.where("id = 2")).to_a + assert_equal expected, Post.order(Arel.sql("body asc")).where("id = 1").unscope(:order).or(Post.where("id = 2")).to_a end def test_or_with_incompatible_unscope error = assert_raises ArgumentError do - Post.order("body asc").where("id = 1").or(Post.order("body asc").where("id = 2").unscope(:order)).to_a + Post.order(Arel.sql("body asc")).where("id = 1").or(Post.order(Arel.sql("body asc")).where("id = 2").unscope(:order)).to_a end assert_equal "Relation passed to #or must be structurally compatible. Incompatible values: [:order]", error.message @@ -101,7 +101,7 @@ module ActiveRecord end def test_or_inside_named_scope - expected = Post.where("body LIKE '\%a\%' OR title LIKE ?", "%'%").order("id DESC").to_a + expected = Post.where("body LIKE '\%a\%' OR title LIKE ?", "%'%").order(Arel.sql("id DESC")).to_a assert_equal expected, Post.order(id: :desc).typographically_interesting end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 72433d1e8e..906f3499dd 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -102,7 +102,7 @@ class RelationTest < ActiveRecord::TestCase end def test_scoped_first - topics = Topic.all.order("id ASC") + topics = Topic.all.order(Arel.sql("id ASC")) assert_queries(1) do 2.times { assert_equal "The First Topic", topics.first.title } @@ -112,7 +112,7 @@ class RelationTest < ActiveRecord::TestCase end def test_loaded_first - topics = Topic.all.order("id ASC") + topics = Topic.all.order(Arel.sql("id ASC")) topics.load # force load assert_no_queries do @@ -123,7 +123,7 @@ class RelationTest < ActiveRecord::TestCase end def test_loaded_first_with_limit - topics = Topic.all.order("id ASC") + topics = Topic.all.order(Arel.sql("id ASC")) topics.load # force load assert_no_queries do @@ -135,7 +135,7 @@ class RelationTest < ActiveRecord::TestCase end def test_first_get_more_than_available - topics = Topic.all.order("id ASC") + topics = Topic.all.order(Arel.sql("id ASC")) unloaded_first = topics.first(10) topics.load # force load @@ -238,7 +238,7 @@ class RelationTest < ActiveRecord::TestCase end def test_reverse_order_with_function - topics = Topic.order("length(title)").reverse_order + topics = Topic.order(Arel.sql("length(title)")).reverse_order assert_equal topics(:second).title, topics.first.title end @@ -248,24 +248,24 @@ class RelationTest < ActiveRecord::TestCase end def test_reverse_order_with_function_other_predicates - topics = Topic.order("author_name, length(title), id").reverse_order + topics = Topic.order(Arel.sql("author_name, length(title), id")).reverse_order assert_equal topics(:second).title, topics.first.title - topics = Topic.order("length(author_name), id, length(title)").reverse_order + topics = Topic.order(Arel.sql("length(author_name), id, length(title)")).reverse_order assert_equal topics(:fifth).title, topics.first.title end def test_reverse_order_with_multiargument_function assert_raises(ActiveRecord::IrreversibleOrderError) do - Topic.order("concat(author_name, title)").reverse_order + Topic.order(Arel.sql("concat(author_name, title)")).reverse_order end assert_raises(ActiveRecord::IrreversibleOrderError) do - Topic.order("concat(lower(author_name), title)").reverse_order + Topic.order(Arel.sql("concat(lower(author_name), title)")).reverse_order end assert_raises(ActiveRecord::IrreversibleOrderError) do - Topic.order("concat(author_name, lower(title))").reverse_order + Topic.order(Arel.sql("concat(author_name, lower(title))")).reverse_order end assert_raises(ActiveRecord::IrreversibleOrderError) do - Topic.order("concat(lower(author_name), title, length(title)").reverse_order + Topic.order(Arel.sql("concat(lower(author_name), title, length(title)")).reverse_order end end @@ -277,10 +277,10 @@ class RelationTest < ActiveRecord::TestCase def test_reverse_order_with_nulls_first_or_last assert_raises(ActiveRecord::IrreversibleOrderError) do - Topic.order("title NULLS FIRST").reverse_order + Topic.order(Arel.sql("title NULLS FIRST")).reverse_order end assert_raises(ActiveRecord::IrreversibleOrderError) do - Topic.order("title nulls last").reverse_order + Topic.order(Arel.sql("title nulls last")).reverse_order end end @@ -319,7 +319,7 @@ class RelationTest < ActiveRecord::TestCase end def test_raising_exception_on_invalid_hash_params - e = assert_raise(ArgumentError) { Topic.order(:name, "id DESC", id: :asfsdf) } + e = assert_raise(ArgumentError) { Topic.order(Arel.sql("name"), Arel.sql("id DESC"), id: :asfsdf) } assert_equal 'Direction "asfsdf" is invalid. Valid directions are: [:asc, :desc, :ASC, :DESC, "asc", "desc", "ASC", "DESC"]', e.message end @@ -365,7 +365,7 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_order_and_take - entrants = Entrant.order("id ASC").limit(2).to_a + entrants = Entrant.order(Arel.sql("id ASC")).limit(2).to_a assert_equal 2, entrants.size assert_equal entrants(:first).name, entrants.first.name @@ -373,18 +373,21 @@ class RelationTest < ActiveRecord::TestCase def test_finding_with_cross_table_order_and_limit tags = Tag.includes(:taggings). - order("tags.name asc", "taggings.taggable_id asc", "REPLACE('abc', taggings.taggable_type, taggings.taggable_type)"). - limit(1).to_a + order( + Arel.sql("tags.name asc"), + Arel.sql("taggings.taggable_id asc"), + Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)") + ).limit(1).to_a assert_equal 1, tags.length end def test_finding_with_complex_order_and_limit - tags = Tag.includes(:taggings).references(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").limit(1).to_a + tags = Tag.includes(:taggings).references(:taggings).order(Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)")).limit(1).to_a assert_equal 1, tags.length end def test_finding_with_complex_order - tags = Tag.includes(:taggings).references(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").to_a + tags = Tag.includes(:taggings).references(:taggings).order(Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)")).to_a assert_equal 3, tags.length end @@ -400,12 +403,12 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_order_limit_and_offset - entrants = Entrant.order("id ASC").limit(2).offset(1) + entrants = Entrant.order(Arel.sql("id ASC")).limit(2).offset(1) assert_equal 2, entrants.to_a.size assert_equal entrants(:second).name, entrants.first.name - entrants = Entrant.order("id ASC").limit(2).offset(2) + entrants = Entrant.order(Arel.sql("id ASC")).limit(2).offset(2) assert_equal 1, entrants.to_a.size assert_equal entrants(:third).name, entrants.first.name end @@ -504,7 +507,7 @@ class RelationTest < ActiveRecord::TestCase def test_eager_association_loading_of_stis_with_multiple_references authors = Author.eager_load(posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } }). - order("comments.body, very_special_comments_posts.body").where("posts.id = 4").to_a + order(Arel.sql("comments.body, very_special_comments_posts.body")).where("posts.id = 4").to_a assert_equal [authors(:david)], authors assert_no_queries do @@ -515,27 +518,27 @@ class RelationTest < ActiveRecord::TestCase def test_find_with_preloaded_associations assert_queries(2) do - posts = Post.preload(:comments).order("posts.id") + posts = Post.preload(:comments).order(Arel.sql("posts.id")) assert posts.first.comments.first end assert_queries(2) do - posts = Post.preload(:comments).order("posts.id") + posts = Post.preload(:comments).order(Arel.sql("posts.id")) assert posts.first.comments.first end assert_queries(2) do - posts = Post.preload(:author).order("posts.id") + posts = Post.preload(:author).order(Arel.sql("posts.id")) assert posts.first.author end assert_queries(2) do - posts = Post.preload(:author).order("posts.id") + posts = Post.preload(:author).order(Arel.sql("posts.id")) assert posts.first.author end assert_queries(3) do - posts = Post.preload(:author, :comments).order("posts.id") + posts = Post.preload(:author, :comments).order(Arel.sql("posts.id")) assert posts.first.author assert posts.first.comments.first end @@ -550,22 +553,22 @@ class RelationTest < ActiveRecord::TestCase def test_find_with_included_associations assert_queries(2) do - posts = Post.includes(:comments).order("posts.id") + posts = Post.includes(:comments).order(Arel.sql("posts.id")) assert posts.first.comments.first end assert_queries(2) do - posts = Post.all.includes(:comments).order("posts.id") + posts = Post.all.includes(:comments).order(Arel.sql("posts.id")) assert posts.first.comments.first end assert_queries(2) do - posts = Post.includes(:author).order("posts.id") + posts = Post.includes(:author).order(Arel.sql("posts.id")) assert posts.first.author end assert_queries(3) do - posts = Post.includes(:author, :comments).order("posts.id") + posts = Post.includes(:author, :comments).order(Arel.sql("posts.id")) assert posts.first.author assert posts.first.comments.first end @@ -577,7 +580,7 @@ class RelationTest < ActiveRecord::TestCase end def test_includes_with_select - query = Post.select("comments_count AS ranking").order("ranking").includes(:comments) + query = Post.select("comments_count AS ranking").order(Arel.sql("ranking")).includes(:comments) .where(comments: { id: 1 }) assert_equal ["comments_count AS ranking"], query.select_values @@ -646,9 +649,9 @@ class RelationTest < ActiveRecord::TestCase def test_to_sql_on_eager_join expected = assert_sql { - Post.eager_load(:last_comment).order("comments.id DESC").to_a + Post.eager_load(:last_comment).order(Arel.sql("comments.id DESC")).to_a }.first - actual = Post.eager_load(:last_comment).order("comments.id DESC").to_sql + actual = Post.eager_load(:last_comment).order(Arel.sql("comments.id DESC")).to_sql assert_equal expected, actual end @@ -659,7 +662,7 @@ class RelationTest < ActiveRecord::TestCase end def test_loading_with_one_association_with_non_preload - posts = Post.eager_load(:last_comment).order("comments.id DESC") + posts = Post.eager_load(:last_comment).order(Arel.sql("comments.id DESC")) post = posts.find { |p| p.id == 1 } assert_equal Post.find(1).last_comment, post.last_comment end @@ -695,7 +698,7 @@ class RelationTest < ActiveRecord::TestCase end def test_find_ids - authors = Author.order("id ASC") + authors = Author.order(Arel.sql("id ASC")) results = authors.find(authors(:david).id, authors(:mary).id) assert_kind_of Array, results @@ -979,7 +982,7 @@ class RelationTest < ActiveRecord::TestCase end def test_multiple_selects - post = Post.all.select("comments_count").select("title").order("id ASC").first + post = Post.all.select("comments_count").select("title").order(Arel.sql("id ASC")).first assert_equal "Welcome to the weblog", post.title assert_equal 2, post.comments_count end @@ -1341,7 +1344,7 @@ class RelationTest < ActiveRecord::TestCase end def test_except - relation = Post.where(author_id: 1).order("id ASC").limit(1) + relation = Post.where(author_id: 1).order(Arel.sql("id ASC")).limit(1) assert_equal [posts(:welcome)], relation.to_a author_posts = relation.except(:order, :limit) @@ -1352,7 +1355,7 @@ class RelationTest < ActiveRecord::TestCase end def test_only - relation = Post.where(author_id: 1).order("id ASC").limit(1) + relation = Post.where(author_id: 1).order(Arel.sql("id ASC")).limit(1) assert_equal [posts(:welcome)], relation.to_a author_posts = relation.only(:where) @@ -1363,7 +1366,7 @@ class RelationTest < ActiveRecord::TestCase end def test_anonymous_extension - relation = Post.where(author_id: 1).order("id ASC").extending do + relation = Post.where(author_id: 1).order(Arel.sql("id ASC")).extending do def author "lifo" end @@ -1374,7 +1377,7 @@ class RelationTest < ActiveRecord::TestCase end def test_named_extension - relation = Post.where(author_id: 1).order("id ASC").extending(Post::NamedExtension) + relation = Post.where(author_id: 1).order(Arel.sql("id ASC")).extending(Post::NamedExtension) assert_equal "lifo", relation.author assert_equal "lifo", relation.limit(1).author end @@ -1389,13 +1392,13 @@ class RelationTest < ActiveRecord::TestCase end def test_order_using_scoping - car1 = CoolCar.order("id DESC").scoping do - CoolCar.all.merge!(order: "id asc").first + car1 = CoolCar.order(Arel.sql("id DESC")).scoping do + CoolCar.all.merge!(order: Arel.sql("id asc")).first end assert_equal "zyke", car1.name - car2 = FastCar.order("id DESC").scoping do - FastCar.all.merge!(order: "id asc").first + car2 = FastCar.order(Arel.sql("id DESC")).scoping do + FastCar.all.merge!(order: Arel.sql("id asc")).first end assert_equal "zyke", car2.name end @@ -1418,7 +1421,7 @@ class RelationTest < ActiveRecord::TestCase end def test_ordering_with_extra_spaces - assert_equal authors(:david), Author.order("id DESC , name DESC").last + assert_equal authors(:david), Author.order(Arel.sql("id DESC , name DESC")).last end def test_update_all_with_blank_argument @@ -1439,7 +1442,7 @@ class RelationTest < ActiveRecord::TestCase end def test_update_all_with_joins_and_limit_and_order - comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("comments.id").limit(1) + comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order(Arel.sql("comments.id")).limit(1) assert_equal 1, comments.update_all(post_id: posts(:thinking).id) assert_equal posts(:thinking), comments(:greetings).post assert_equal posts(:welcome), comments(:more_greetings).post @@ -1454,7 +1457,7 @@ class RelationTest < ActiveRecord::TestCase end def test_update_all_with_joins_and_offset_and_order - all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("posts.id", "comments.id") + all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order(Arel.sql("posts.id"), Arel.sql("comments.id")) count = all_comments.count comments = all_comments.offset(1) @@ -1564,52 +1567,52 @@ class RelationTest < ActiveRecord::TestCase end def test_automatically_added_order_references - scope = Post.order("comments.body") + scope = Post.order(Arel.sql("comments.body")) assert_equal ["comments"], scope.references_values - scope = Post.order("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}") + scope = Post.order(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")) if current_adapter?(:OracleAdapter) assert_equal ["COMMENTS"], scope.references_values else assert_equal ["comments"], scope.references_values end - scope = Post.order("comments.body", "yaks.body") + scope = Post.order(Arel.sql("comments.body"), Arel.sql("yaks.body")) assert_equal ["comments", "yaks"], scope.references_values # Don't infer yaks, let's not go down that road again... - scope = Post.order("comments.body, yaks.body") + scope = Post.order(Arel.sql("comments.body, yaks.body")) assert_equal ["comments"], scope.references_values - scope = Post.order("comments.body asc") + scope = Post.order(Arel.sql("comments.body asc")) assert_equal ["comments"], scope.references_values - scope = Post.order("foo(comments.body)") + scope = Post.order(Arel.sql("foo(comments.body)")) assert_equal [], scope.references_values end def test_automatically_added_reorder_references - scope = Post.reorder("comments.body") + scope = Post.reorder(Arel.sql("comments.body")) assert_equal %w(comments), scope.references_values - scope = Post.reorder("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}") + scope = Post.reorder(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")) if current_adapter?(:OracleAdapter) assert_equal ["COMMENTS"], scope.references_values else assert_equal ["comments"], scope.references_values end - scope = Post.reorder("comments.body", "yaks.body") + scope = Post.reorder(Arel.sql("comments.body"), Arel.sql("yaks.body")) assert_equal %w(comments yaks), scope.references_values # Don't infer yaks, let's not go down that road again... - scope = Post.reorder("comments.body, yaks.body") + scope = Post.reorder(Arel.sql("comments.body, yaks.body")) assert_equal %w(comments), scope.references_values - scope = Post.reorder("comments.body asc") + scope = Post.reorder(Arel.sql("comments.body asc")) assert_equal %w(comments), scope.references_values - scope = Post.reorder("foo(comments.body)") + scope = Post.reorder(Arel.sql("foo(comments.body)")) assert_equal [], scope.references_values end @@ -1811,7 +1814,7 @@ class RelationTest < ActiveRecord::TestCase end test "joins with select" do - posts = Post.joins(:author).select("id", "authors.author_address_id").order("posts.id").limit(3) + posts = Post.joins(:author).select("id", "authors.author_address_id").order(Arel.sql("posts.id")).limit(3) assert_equal [1, 2, 4], posts.map(&:id) assert_equal [1, 1, 1], posts.map(&:author_address_id) end diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 716ca29eda..26f0b86703 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -13,7 +13,7 @@ class DefaultScopingTest < ActiveRecord::TestCase fixtures :developers, :posts, :comments def test_default_scope - expected = Developer.all.merge!(order: "salary DESC").to_a.collect(&:salary) + expected = Developer.all.merge!(order: Arel.sql("salary DESC")).to_a.collect(&:salary) received = DeveloperOrderedBySalary.all.collect(&:salary) assert_equal expected, received end @@ -80,20 +80,20 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_scope_overwrites_default - expected = Developer.all.merge!(order: "salary DESC, name DESC").to_a.collect(&:name) + expected = Developer.all.merge!(order: Arel.sql("salary DESC, name DESC")).to_a.collect(&:name) received = DeveloperOrderedBySalary.by_name.to_a.collect(&:name) assert_equal expected, received end def test_reorder_overrides_default_scope_order - expected = Developer.order("name DESC").collect(&:name) - received = DeveloperOrderedBySalary.reorder("name DESC").collect(&:name) + expected = Developer.order(Arel.sql("name DESC")).collect(&:name) + received = DeveloperOrderedBySalary.reorder(Arel.sql("name DESC")).collect(&:name) assert_equal expected, received end def test_order_after_reorder_combines_orders - expected = Developer.order("name DESC, id DESC").collect { |dev| [dev.name, dev.id] } - received = Developer.order("name ASC").reorder("name DESC").order("id DESC").collect { |dev| [dev.name, dev.id] } + expected = Developer.order(Arel.sql("name DESC, id DESC")).collect { |dev| [dev.name, dev.id] } + received = Developer.order(Arel.sql("name ASC")).reorder(Arel.sql("name DESC")).order(Arel.sql("id DESC")).collect { |dev| [dev.name, dev.id] } assert_equal expected, received end @@ -104,69 +104,69 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_unscope_after_reordering_and_combining - expected = Developer.order("id DESC, name DESC").collect { |dev| [dev.name, dev.id] } - received = DeveloperOrderedBySalary.reorder("name DESC").unscope(:order).order("id DESC, name DESC").collect { |dev| [dev.name, dev.id] } + expected = Developer.order(Arel.sql("id DESC, name DESC")).collect { |dev| [dev.name, dev.id] } + received = DeveloperOrderedBySalary.reorder(Arel.sql("name DESC")).unscope(:order).order(Arel.sql("id DESC, name DESC")).collect { |dev| [dev.name, dev.id] } assert_equal expected, received expected_2 = Developer.all.collect { |dev| [dev.name, dev.id] } - received_2 = Developer.order("id DESC, name DESC").unscope(:order).collect { |dev| [dev.name, dev.id] } + received_2 = Developer.order(Arel.sql("id DESC, name DESC")).unscope(:order).collect { |dev| [dev.name, dev.id] } assert_equal expected_2, received_2 expected_3 = Developer.all.collect { |dev| [dev.name, dev.id] } - received_3 = Developer.reorder("name DESC").unscope(:order).collect { |dev| [dev.name, dev.id] } + received_3 = Developer.reorder(Arel.sql("name DESC")).unscope(:order).collect { |dev| [dev.name, dev.id] } assert_equal expected_3, received_3 end def test_unscope_with_where_attributes - expected = Developer.order("salary DESC").collect(&:name) + expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) received = DeveloperOrderedBySalary.where(name: "David").unscope(where: :name).collect(&:name) assert_equal expected, received - expected_2 = Developer.order("salary DESC").collect(&:name) + expected_2 = Developer.order(Arel.sql("salary DESC")).collect(&:name) received_2 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope({ where: :name }, :select).collect(&:name) assert_equal expected_2, received_2 - expected_3 = Developer.order("salary DESC").collect(&:name) + expected_3 = Developer.order(Arel.sql("salary DESC")).collect(&:name) received_3 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope(:select, :where).collect(&:name) assert_equal expected_3, received_3 - expected_4 = Developer.order("salary DESC").collect(&:name) + expected_4 = Developer.order(Arel.sql("salary DESC")).collect(&:name) received_4 = DeveloperOrderedBySalary.where.not("name" => "Jamis").unscope(where: :name).collect(&:name) assert_equal expected_4, received_4 - expected_5 = Developer.order("salary DESC").collect(&:name) + expected_5 = Developer.order(Arel.sql("salary DESC")).collect(&:name) received_5 = DeveloperOrderedBySalary.where.not("name" => ["Jamis", "David"]).unscope(where: :name).collect(&:name) assert_equal expected_5, received_5 - expected_6 = Developer.order("salary DESC").collect(&:name) + expected_6 = Developer.order(Arel.sql("salary DESC")).collect(&:name) received_6 = DeveloperOrderedBySalary.where(Developer.arel_table["name"].eq("David")).unscope(where: :name).collect(&:name) assert_equal expected_6, received_6 - expected_7 = Developer.order("salary DESC").collect(&:name) + expected_7 = Developer.order(Arel.sql("salary DESC")).collect(&:name) received_7 = DeveloperOrderedBySalary.where(Developer.arel_table[:name].eq("David")).unscope(where: :name).collect(&:name) assert_equal expected_7, received_7 end def test_unscope_comparison_where_clauses # unscoped for WHERE (`developers`.`id` <= 2) - expected = Developer.order("salary DESC").collect(&:name) + expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) received = DeveloperOrderedBySalary.where(id: -Float::INFINITY..2).unscope(where: :id).collect { |dev| dev.name } assert_equal expected, received # unscoped for WHERE (`developers`.`id` < 2) - expected = Developer.order("salary DESC").collect(&:name) + expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) received = DeveloperOrderedBySalary.where(id: -Float::INFINITY...2).unscope(where: :id).collect { |dev| dev.name } assert_equal expected, received end def test_unscope_multiple_where_clauses - expected = Developer.order("salary DESC").collect(&:name) + expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) received = DeveloperOrderedBySalary.where(name: "Jamis").where(id: 1).unscope(where: [:name, :id]).collect(&:name) assert_equal expected, received end def test_unscope_string_where_clauses_involved - dev_relation = Developer.order("salary DESC").where("created_at > ?", 1.year.ago) + dev_relation = Developer.order(Arel.sql("salary DESC")).where("created_at > ?", 1.year.ago) expected = dev_relation.collect(&:name) dev_ordered_relation = DeveloperOrderedBySalary.where(name: "Jamis").where("created_at > ?", 1.year.ago) @@ -176,35 +176,35 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_unscope_with_grouping_attributes - expected = Developer.order("salary DESC").collect(&:name) + expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect(&:name) assert_equal expected, received - expected_2 = Developer.order("salary DESC").collect(&:name) + expected_2 = Developer.order(Arel.sql("salary DESC")).collect(&:name) received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect(&:name) assert_equal expected_2, received_2 end def test_unscope_with_limit_in_query - expected = Developer.order("salary DESC").collect(&:name) + expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect(&:name) assert_equal expected, received end def test_order_to_unscope_reordering - scope = DeveloperOrderedBySalary.order("salary DESC, name ASC").reverse_order.unscope(:order) + scope = DeveloperOrderedBySalary.order(Arel.sql("salary DESC, name ASC")).reverse_order.unscope(:order) assert !/order/i.match?(scope.to_sql) end def test_unscope_reverse_order expected = Developer.all.collect(&:name) - received = Developer.order("salary DESC").reverse_order.unscope(:order).collect(&:name) + received = Developer.order(Arel.sql("salary DESC")).reverse_order.unscope(:order).collect(&:name) assert_equal expected, received end def test_unscope_select - expected = Developer.order("salary ASC").collect(&:name) - received = Developer.order("salary DESC").reverse_order.select(:name).unscope(:select).collect(&:name) + expected = Developer.order(Arel.sql("salary ASC")).collect(&:name) + received = Developer.order(Arel.sql("salary DESC")).reverse_order.select(:name).unscope(:select).collect(&:name) assert_equal expected, received expected_2 = Developer.all.collect(&:id) @@ -256,11 +256,11 @@ class DefaultScopingTest < ActiveRecord::TestCase end assert_raises(ArgumentError) do - Developer.order("name DESC").reverse_order.unscope(:reverse_order) + Developer.order(Arel.sql("name DESC")).reverse_order.unscope(:reverse_order) end assert_raises(ArgumentError) do - Developer.order("name DESC").where(name: "Jamis").unscope() + Developer.order(Arel.sql("name DESC")).where(name: "Jamis").unscope() end end @@ -295,7 +295,7 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_order_in_default_scope_should_not_prevail - expected = Developer.all.merge!(order: "salary desc").to_a.collect(&:salary) + expected = Developer.all.merge!(order: Arel.sql("salary desc")).to_a.collect(&:salary) received = DeveloperOrderedBySalary.all.merge!(order: "salary").to_a.collect(&:salary) assert_equal expected, received end diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index b0431a4e34..4079bb477e 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -472,7 +472,7 @@ class NamedScopingTest < ActiveRecord::TestCase def test_scopes_on_relations # Topic.replied - approved_topics = Topic.all.approved.order("id DESC") + approved_topics = Topic.all.approved.order(Arel.sql("id DESC")) assert_equal topics(:fifth), approved_topics.first replied_approved_topics = approved_topics.replied @@ -480,7 +480,7 @@ class NamedScopingTest < ActiveRecord::TestCase end def test_index_on_scope - approved = Topic.approved.order("id ASC") + approved = Topic.approved.order(Arel.sql("id ASC")) assert_equal topics(:second), approved[0] assert approved.loaded? end diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index 116f8e83aa..2a95204d0d 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -39,23 +39,23 @@ class RelationScopingTest < ActiveRecord::TestCase end def test_reverse_order - assert_equal Developer.order("id DESC").to_a.reverse, Developer.order("id DESC").reverse_order + assert_equal Developer.order(Arel.sql("id DESC")).to_a.reverse, Developer.order(Arel.sql("id DESC")).reverse_order end def test_reverse_order_with_arel_node - assert_equal Developer.order("id DESC").to_a.reverse, Developer.order(Developer.arel_table[:id].desc).reverse_order + assert_equal Developer.order(Arel.sql("id DESC")).to_a.reverse, Developer.order(Developer.arel_table[:id].desc).reverse_order end def test_reverse_order_with_multiple_arel_nodes - assert_equal Developer.order("id DESC").order("name DESC").to_a.reverse, Developer.order(Developer.arel_table[:id].desc).order(Developer.arel_table[:name].desc).reverse_order + assert_equal Developer.order(Arel.sql("id DESC")).order(Arel.sql("name DESC")).to_a.reverse, Developer.order(Developer.arel_table[:id].desc).order(Developer.arel_table[:name].desc).reverse_order end def test_reverse_order_with_arel_nodes_and_strings - assert_equal Developer.order("id DESC").order("name DESC").to_a.reverse, Developer.order("id DESC").order(Developer.arel_table[:name].desc).reverse_order + assert_equal Developer.order(Arel.sql("id DESC")).order(Arel.sql("name DESC")).to_a.reverse, Developer.order(Arel.sql("id DESC")).order(Developer.arel_table[:name].desc).reverse_order end def test_double_reverse_order_produces_original_order - assert_equal Developer.order("name DESC"), Developer.order("name DESC").reverse_order.reverse_order + assert_equal Developer.order(Arel.sql("name DESC")), Developer.order(Arel.sql("name DESC")).reverse_order.reverse_order end def test_scoped_find @@ -72,7 +72,7 @@ class RelationScopingTest < ActiveRecord::TestCase end def test_scoped_find_last - highest_salary = Developer.order("salary DESC").first + highest_salary = Developer.order(Arel.sql("salary DESC")).first Developer.order("salary").scoping do assert_equal highest_salary, Developer.last @@ -80,8 +80,8 @@ class RelationScopingTest < ActiveRecord::TestCase end def test_scoped_find_last_preserves_scope - lowest_salary = Developer.order("salary ASC").first - highest_salary = Developer.order("salary DESC").first + lowest_salary = Developer.order(Arel.sql("salary ASC")).first + highest_salary = Developer.order(Arel.sql("salary DESC")).first Developer.order("salary").scoping do assert_equal highest_salary, Developer.last diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index cb8686f315..11fe073ab0 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -7,10 +7,10 @@ class Author < ActiveRecord::Base has_many :very_special_comments, through: :posts has_many :posts_with_comments, -> { includes(:comments) }, class_name: "Post" has_many :popular_grouped_posts, -> { includes(:comments).group("type").having("SUM(comments_count) > 1").select("type") }, class_name: "Post" - has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order("comments.id") }, class_name: "Post" - has_many :posts_sorted_by_id_limited, -> { order("posts.id").limit(1) }, class_name: "Post" + has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order(Arel.sql("comments.id")) }, class_name: "Post" + has_many :posts_sorted_by_id_limited, -> { order(Arel.sql("posts.id")).limit(1) }, class_name: "Post" has_many :posts_with_categories, -> { includes(:categories) }, class_name: "Post" - has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, class_name: "Post" + has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order(Arel.sql("posts.id")) }, class_name: "Post" has_many :posts_with_special_categorizations, class_name: "PostWithSpecialCategorization" has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, class_name: "Post" has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, class_name: "Post" @@ -20,15 +20,15 @@ class Author < ActiveRecord::Base end end has_many :comments_containing_the_letter_e, through: :posts, source: :comments - has_many :comments_with_order_and_conditions, -> { order("comments.body").where("comments.body like 'Thank%'") }, through: :posts, source: :comments + has_many :comments_with_order_and_conditions, -> { order(Arel.sql("comments.body")).where("comments.body like 'Thank%'") }, through: :posts, source: :comments has_many :comments_with_include, -> { includes(:post).where(posts: { type: "Post" }) }, through: :posts, source: :comments has_many :comments_for_first_author, -> { for_first_author }, through: :posts, source: :comments has_many :first_posts - has_many :comments_on_first_posts, -> { order("posts.id desc, comments.id asc") }, through: :first_posts, source: :comments + has_many :comments_on_first_posts, -> { order(Arel.sql("posts.id desc, comments.id asc")) }, through: :first_posts, source: :comments has_one :first_post - has_one :comment_on_first_post, -> { order("posts.id desc, comments.id asc") }, through: :first_post, source: :comments + has_one :comment_on_first_post, -> { order(Arel.sql("posts.id desc, comments.id asc")) }, through: :first_post, source: :comments has_many :thinking_posts, -> { where(title: "So I was thinking") }, dependent: :delete_all, class_name: "Post" has_many :welcome_posts, -> { where(title: "Welcome to the weblog") }, class_name: "Post" @@ -40,11 +40,11 @@ class Author < ActiveRecord::Base -> { where(title: "Welcome to the weblog").where(Post.arel_table[:comments_count].gt(0)) }, class_name: "Post" - has_many :comments_desc, -> { order("comments.id DESC") }, through: :posts, source: :comments + has_many :comments_desc, -> { order(Arel.sql("comments.id DESC")) }, through: :posts, source: :comments has_many :unordered_comments, -> { unscope(:order).distinct }, through: :posts_sorted_by_id_limited, source: :comments has_many :funky_comments, through: :posts, source: :comments - has_many :ordered_uniq_comments, -> { distinct.order("comments.id") }, through: :posts, source: :comments - has_many :ordered_uniq_comments_desc, -> { distinct.order("comments.id DESC") }, through: :posts, source: :comments + has_many :ordered_uniq_comments, -> { distinct.order(Arel.sql("comments.id")) }, through: :posts, source: :comments + has_many :ordered_uniq_comments_desc, -> { distinct.order(Arel.sql("comments.id DESC")) }, through: :posts, source: :comments has_many :readonly_comments, -> { readonly }, through: :posts, source: :comments has_many :special_posts @@ -107,15 +107,15 @@ class Author < ActiveRecord::Base has_many :similar_posts, -> { distinct }, through: :tags, source: :tagged_posts has_many :ordered_posts, -> { distinct }, through: :ordered_tags, source: :tagged_posts - has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, through: :posts, source: :tags + has_many :distinct_tags, -> { select("DISTINCT tags.*").order(Arel.sql("tags.name")) }, through: :posts, source: :tags has_many :tags_with_primary_key, through: :posts has_many :books has_many :unpublished_books, -> { where(status: [:proposed, :written]) }, class_name: "Book" has_many :subscriptions, through: :books - has_many :subscribers, -> { order("subscribers.nick") }, through: :subscriptions - has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order("subscribers.nick") }, through: :subscriptions, source: :subscriber + has_many :subscribers, -> { order(Arel.sql("subscribers.nick")) }, through: :subscriptions + has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order(Arel.sql("subscribers.nick")) }, through: :subscriptions, source: :subscriber has_one :essay, primary_key: :name, as: :writer has_one :essay_category, through: :essay, source: :category diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb index 3d6a7a96c2..0be943a321 100644 --- a/activerecord/test/models/car.rb +++ b/activerecord/test/models/car.rb @@ -19,13 +19,13 @@ class Car < ActiveRecord::Base scope :incl_tyres, -> { includes(:tyres) } scope :incl_engines, -> { includes(:engines) } - scope :order_using_new_style, -> { order("name asc") } + scope :order_using_new_style, -> { order(Arel.sql("name asc")) } end class CoolCar < Car - default_scope { order("name desc") } + default_scope { order(Arel.sql("name desc")) } end class FastCar < Car - default_scope { order("name desc") } + default_scope { order(Arel.sql("name desc")) } end diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb index 2ccc00bed9..3038264694 100644 --- a/activerecord/test/models/category.rb +++ b/activerecord/test/models/category.rb @@ -4,7 +4,7 @@ class Category < ActiveRecord::Base has_and_belongs_to_many :posts has_and_belongs_to_many :special_posts, class_name: "Post" has_and_belongs_to_many :other_posts, class_name: "Post" - has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, -> { includes(:authors).order("authors.id") }, class_name: "Post" + has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, -> { includes(:authors).order(Arel.sql("authors.id")) }, class_name: "Post" has_and_belongs_to_many :select_testing_posts, -> { select "posts.*, 1 as correctness_marker" }, diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index 5ab433f2d9..d5acfc0749 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -81,7 +81,7 @@ class CommentThatAutomaticallyAltersPostBody < Comment end class CommentWithDefaultScopeReferencesAssociation < Comment - default_scope -> { includes(:developer).order("developers.name").references(:developer) } + default_scope -> { includes(:developer).order(Arel.sql("developers.name")).references(:developer) } belongs_to :developer end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index bbc5fc2b2d..1a82a9e646 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -50,7 +50,7 @@ class Firm < Company has_many :clients, -> { order "id" }, dependent: :destroy, before_remove: :log_before_remove, after_remove: :log_after_remove has_many :unsorted_clients, class_name: "Client" has_many :unsorted_clients_with_symbol, class_name: :Client - has_many :clients_sorted_desc, -> { order "id DESC" }, class_name: "Client" + has_many :clients_sorted_desc, -> { order Arel.sql("id DESC") }, class_name: "Client" has_many :clients_of_firm, -> { order "id" }, foreign_key: "client_of", class_name: "Client", inverse_of: :firm has_many :clients_ordered_by_name, -> { order "name" }, class_name: "Client" has_many :unvalidated_clients_of_firm, foreign_key: "client_of", class_name: "Client", validate: false diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index 52b7e06a63..9108eb5249 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -9,7 +9,7 @@ module MyApplication class Firm < Company has_many :clients, -> { order("id") }, dependent: :destroy - has_many :clients_sorted_desc, -> { order("id DESC") }, class_name: "Client" + has_many :clients_sorted_desc, -> { order(Arel.sql("id DESC")) }, class_name: "Client" has_many :clients_of_firm, -> { order "id" }, foreign_key: "client_of", class_name: "Client" has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, class_name: "Client" has_one :account, class_name: "MyApplication::Billing::Account", dependent: :destroy diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 8881c69368..c2a21eac04 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -4,7 +4,7 @@ require "ostruct" module DeveloperProjectsAssociationExtension2 def find_least_recent - order("id ASC").first + order(Arel.sql("id ASC")).first end end @@ -13,7 +13,7 @@ class Developer < ActiveRecord::Base has_and_belongs_to_many :projects do def find_most_recent - order("id DESC").first + order(Arel.sql("id DESC")).first end end @@ -41,7 +41,7 @@ class Developer < ActiveRecord::Base join_table: "developers_projects", association_foreign_key: "project_id" do def find_least_recent - order("id ASC").first + order(Arel.sql("id ASC")).first end end @@ -126,7 +126,7 @@ end class DeveloperFilteredOnJoins < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" def self.default_scope joins(:projects).where(projects: { name: "Active Controller" }) @@ -135,9 +135,9 @@ end class DeveloperOrderedBySalary < ActiveRecord::Base self.table_name = "developers" - default_scope { order("salary DESC") } + default_scope { order(Arel.sql("salary DESC")) } - scope :by_name, -> { order("name DESC") } + scope :by_name, -> { order(Arel.sql("name DESC")) } end class DeveloperCalledDavid < ActiveRecord::Base @@ -225,14 +225,14 @@ end class EagerDeveloperWithDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" default_scope { includes(:projects) } end class EagerDeveloperWithClassMethodDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" def self.default_scope includes(:projects) @@ -241,21 +241,21 @@ end class EagerDeveloperWithLambdaDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" default_scope lambda { includes(:projects) } end class EagerDeveloperWithBlockDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" default_scope { includes(:projects) } end class EagerDeveloperWithCallableDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" default_scope OpenStruct.new(call: includes(:projects)) end diff --git a/activerecord/test/models/membership.rb b/activerecord/test/models/membership.rb index 09ee7544b3..47cb4d2146 100644 --- a/activerecord/test/models/membership.rb +++ b/activerecord/test/models/membership.rb @@ -12,7 +12,7 @@ class CurrentMembership < Membership end class SuperMembership < Membership - belongs_to :member, -> { order("members.id DESC") } + belongs_to :member, -> { order(Arel.sql("members.id DESC")) } belongs_to :club end diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb index 5fa50d9918..ebaafdec5e 100644 --- a/activerecord/test/models/owner.rb +++ b/activerecord/test/models/owner.rb @@ -2,7 +2,7 @@ class Owner < ActiveRecord::Base self.primary_key = :owner_id - has_many :pets, -> { order "pets.name desc" } + has_many :pets, -> { order Arel.sql("pets.name desc") } has_many :toys, through: :pets has_many :persons, through: :pets diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 5cba1e440e..7067f1b6b0 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -19,7 +19,7 @@ class Person < ActiveRecord::Base has_many :bad_references has_many :fixed_bad_references, -> { where favourite: true }, class_name: "BadReference" has_one :favourite_reference, -> { where "favourite=?", true }, class_name: "Reference" - has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order("comments.id") }, through: :readers, source: :post + has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order(Arel.sql("comments.id")) }, through: :readers, source: :post has_many :first_posts, -> { where(id: [1, 2]) }, through: :readers has_many :jobs, through: :references diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index c8617d1cfe..99de37a52f 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -3,7 +3,7 @@ class Pirate < ActiveRecord::Base belongs_to :parrot, validate: true belongs_to :non_validated_parrot, class_name: "Parrot" - has_and_belongs_to_many :parrots, -> { order("parrots.id ASC") }, validate: true + has_and_belongs_to_many :parrots, -> { order(Arel.sql("parrots.id ASC")) }, validate: true has_and_belongs_to_many :non_validated_parrots, class_name: "Parrot" has_and_belongs_to_many :parrots_with_method_callbacks, class_name: "Parrot", before_add: :log_before_add, @@ -23,7 +23,7 @@ class Pirate < ActiveRecord::Base has_one :ship has_one :update_only_ship, class_name: "Ship" has_one :non_validated_ship, class_name: "Ship" - has_many :birds, -> { order("birds.id ASC") } + has_many :birds, -> { order(Arel.sql("birds.id ASC")) } has_many :birds_with_method_callbacks, class_name: "Bird", before_add: :log_before_add, after_add: :log_after_add, diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 7f064bf3dd..49bbbaaab7 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -35,8 +35,8 @@ class Post < ActiveRecord::Base def first_comment super.body end - has_one :first_comment, -> { order("id ASC") }, class_name: "Comment" - has_one :last_comment, -> { order("id desc") }, class_name: "Comment" + has_one :first_comment, -> { order(Arel.sql("id ASC")) }, class_name: "Comment" + has_one :last_comment, -> { order(Arel.sql("id desc")) }, class_name: "Comment" scope :with_special_comments, -> { joins(:comments).where(comments: { type: "SpecialComment" }) } scope :with_very_special_comments, -> { joins(:comments).where(comments: { type: "VerySpecialComment" }) } @@ -52,7 +52,7 @@ class Post < ActiveRecord::Base has_many :comments do def find_most_recent - order("id DESC").first + order(Arel.sql("id DESC")).first end def newest @@ -85,7 +85,7 @@ class Post < ActiveRecord::Base has_one :very_special_comment has_one :very_special_comment_with_post, -> { includes(:post) }, class_name: "VerySpecialComment" - has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order("posts.id") }, class_name: "VerySpecialComment" + has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order(Arel.sql("posts.id")) }, class_name: "VerySpecialComment" has_many :special_comments has_many :nonexistent_comments, -> { where "comments.id < 0" }, class_name: "Comment" @@ -319,5 +319,9 @@ class FakeKlass def arel_attribute(name, table) table[name] end + + def enforce_raw_sql_whitelist(*args) + # noop + end end end diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index 846cef625b..9b282a6729 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -2,9 +2,9 @@ class Project < ActiveRecord::Base belongs_to :mentor - has_and_belongs_to_many :developers, -> { distinct.order "developers.name desc, developers.id desc" } + has_and_belongs_to_many :developers, -> { distinct.order Arel.sql("developers.name desc, developers.id desc") } has_and_belongs_to_many :readonly_developers, -> { readonly }, class_name: "Developer" - has_and_belongs_to_many :non_unique_developers, -> { order "developers.name desc, developers.id desc" }, class_name: "Developer" + has_and_belongs_to_many :non_unique_developers, -> { order Arel.sql("developers.name desc, developers.id desc") }, class_name: "Developer" has_and_belongs_to_many :limited_developers, -> { limit 1 }, class_name: "Developer" has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, class_name: "Developer" has_and_belongs_to_many :developers_named_david_with_hash_conditions, -> { where(name: "David").distinct }, class_name: "Developer" diff --git a/activerecord/test/models/tag.rb b/activerecord/test/models/tag.rb index bc13c3a42d..e0d42f4f66 100644 --- a/activerecord/test/models/tag.rb +++ b/activerecord/test/models/tag.rb @@ -11,6 +11,6 @@ end class OrderedTag < Tag self.table_name = "tags" - has_many :taggings, -> { order("taggings.id DESC") }, foreign_key: "tag_id" + has_many :taggings, -> { order(Arel.sql("taggings.id DESC")) }, foreign_key: "tag_id" has_many :tagged_posts, through: :taggings, source: "taggable", source_type: "Post" end -- cgit v1.2.3 From 5180fe2cd8233169935065efe8762bd5d7b2709c Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Tue, 26 Sep 2017 09:29:24 -0600 Subject: allow table name and direction in string order arg --- .../lib/active_record/relation/calculations.rb | 31 +++------ .../lib/active_record/relation/query_methods.rb | 26 +++++++- .../associations/cascaded_eager_loading_test.rb | 28 ++++---- activerecord/test/cases/associations/eager_test.rb | 78 +++++++++++----------- .../associations/has_many_associations_test.rb | 8 +-- .../has_many_through_associations_test.rb | 4 +- .../test/cases/associations/join_model_test.rb | 30 ++++----- .../nested_through_associations_test.rb | 24 +++---- activerecord/test/cases/base_test.rb | 18 ++--- activerecord/test/cases/batches_test.rb | 14 ++-- activerecord/test/cases/finder_test.rb | 22 +++--- activerecord/test/cases/relation/merging_test.rb | 8 +-- activerecord/test/cases/relation/or_test.rb | 14 ++-- activerecord/test/cases/relations_test.rb | 66 +++++++++--------- .../test/cases/scoping/default_scoping_test.rb | 52 +++++++-------- .../test/cases/scoping/named_scoping_test.rb | 4 +- .../test/cases/scoping/relation_scoping_test.rb | 16 ++--- activerecord/test/cases/unsafe_raw_sql_test.rb | 42 +++++++++++- activerecord/test/models/author.rb | 4 +- activerecord/test/models/car.rb | 6 +- activerecord/test/models/company.rb | 2 +- activerecord/test/models/company_in_module.rb | 2 +- activerecord/test/models/developer.rb | 22 +++--- activerecord/test/models/membership.rb | 2 +- activerecord/test/models/pirate.rb | 4 +- activerecord/test/models/post.rb | 10 ++- activerecord/test/models/tag.rb | 2 +- 27 files changed, 295 insertions(+), 244 deletions(-) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 236d36e15f..75795fe493 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -180,14 +180,15 @@ module ActiveRecord end if has_include?(column_names.first) - construct_relation_for_association_calculations.pluck(*column_names) + relation = apply_join_dependency + relation.pluck(*column_names) else - enforce_raw_sql_whitelist(column_names) + enforce_raw_sql_whitelist(column_names, whitelist: allowed_pluck_columns) relation = spawn relation.select_values = column_names.map { |cn| @klass.respond_to_attribute?(cn) ? arel_attribute(cn) : cn } - result = klass.connection.select_all(relation.arel, nil, bound_attributes) + result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) } result.cast_values(klass.attribute_types) end end @@ -202,26 +203,10 @@ module ActiveRecord private - def _pluck(column_names, unsafe_raw) - unrecognized = column_names.reject do |cn| - @klass.respond_to_attribute?(cn) - end - - if loaded? && unrecognized.none? - records.pluck(*column_names) - elsif has_include?(column_names.first) - relation = apply_join_dependency - relation.pluck(*column_names) - elsif unsafe_raw || unrecognized.none? - relation = spawn - relation.select_values = column_names.map { |cn| - @klass.respond_to_attribute?(cn) ? arel_attribute(cn) : cn - } - result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) } - result.cast_values(klass.attribute_types) - else - raise ArgumentError, "Invalid column name: #{unrecognized}" - end + def allowed_pluck_columns + @klass.attribute_names_and_aliases.map do |name| + [name, "#{table_name}.#{name}"] + end.flatten end def has_include?(column_name) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index f3b44d19d6..094e5aa733 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -297,7 +297,11 @@ module ActiveRecord # Same as #order but operates on relation in-place instead of copying. def order!(*args) # :nodoc: - @klass.enforce_raw_sql_whitelist(column_names_from_order_arguments(args)) + @klass.enforce_raw_sql_whitelist( + column_names_from_order_arguments(args), + whitelist: allowed_order_columns + ) + preprocess_order_args(args) self.order_values += args @@ -320,7 +324,11 @@ module ActiveRecord # Same as #reorder but operates on relation in-place instead of copying. def reorder!(*args) # :nodoc: - @klass.enforce_raw_sql_whitelist(column_names_from_order_arguments(args)) + @klass.enforce_raw_sql_whitelist( + column_names_from_order_arguments(args), + whitelist: allowed_order_columns + ) + preprocess_order_args(args) self.reordering_value = true @@ -920,6 +928,20 @@ module ActiveRecord private + def allowed_order_columns + @klass.attribute_names_and_aliases.map do |name| + [name, "#{table_name}.#{name}"].map do |name| + [ + name, + "#{name} asc", + "#{name} ASC", + "#{name} desc", + "#{name} DESC" + ] + end + end.flatten + end + # Extract column names from arguments passed to #order or #reorder. def column_names_from_order_arguments(args) args.flat_map { |arg| arg.is_a?(Hash) ? arg.keys : arg } diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 3eba5ed466..f08fd73da4 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -18,7 +18,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase :categorizations, :people, :categories, :edges, :vertices def test_eager_association_loading_with_cascaded_two_levels - authors = Author.all.merge!(includes: { posts: :comments }, order: Arel.sql("authors.id")).to_a + authors = Author.all.merge!(includes: { posts: :comments }, order: "authors.id").to_a assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal 3, authors[1].posts.size @@ -26,7 +26,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_cascaded_two_levels_and_one_level - authors = Author.all.merge!(includes: [{ posts: :comments }, :categorizations], order: Arel.sql("authors.id")).to_a + authors = Author.all.merge!(includes: [{ posts: :comments }, :categorizations], order: "authors.id").to_a assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal 3, authors[1].posts.size @@ -42,7 +42,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_grafts_stashed_associations_to_correct_parent - assert_equal people(:michael), Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order(Arel.sql("people.id")).first + assert_equal people(:michael), Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order("people.id").first end def test_cascaded_eager_association_loading_with_join_for_count @@ -78,7 +78,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations - authors = Author.all.merge!(includes: { posts: [:comments, :categorizations] }, order: Arel.sql("authors.id")).to_a + authors = Author.all.merge!(includes: { posts: [:comments, :categorizations] }, order: "authors.id").to_a assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal 3, authors[1].posts.size @@ -86,7 +86,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference - authors = Author.all.merge!(includes: { posts: [:comments, :author] }, order: Arel.sql("authors.id")).to_a + authors = Author.all.merge!(includes: { posts: [:comments, :author] }, order: "authors.id").to_a assert_equal 3, authors.size assert_equal 5, authors[0].posts.size assert_equal authors(:david).name, authors[0].name @@ -94,13 +94,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_cascaded_two_levels_with_condition - authors = Author.all.merge!(includes: { posts: :comments }, where: "authors.id=1", order: Arel.sql("authors.id")).to_a + authors = Author.all.merge!(includes: { posts: :comments }, where: "authors.id=1", order: "authors.id").to_a assert_equal 1, authors.size assert_equal 5, authors[0].posts.size end def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong - firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: Arel.sql("companies.id")).to_a + firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: "companies.id").to_a assert_equal 2, firms.size assert_equal firms.first.account, firms.first.account.firm.account assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account } @@ -108,7 +108,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_has_many_sti - topics = Topic.all.merge!(includes: :replies, order: Arel.sql("topics.id")).to_a + topics = Topic.all.merge!(includes: :replies, order: "topics.id").to_a first, second, = topics(:first).replies.size, topics(:second).replies.size assert_no_queries do assert_equal first, topics[0].replies.size @@ -121,7 +121,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase silly.parent_id = 1 assert silly.save - topics = Topic.all.merge!(includes: :replies, order: [Arel.sql("topics.id"), Arel.sql("replies_topics.id")]).to_a + topics = Topic.all.merge!(includes: :replies, order: ["topics.id", Arel.sql("replies_topics.id")]).to_a assert_no_queries do assert_equal 2, topics[0].replies.size assert_equal 0, topics[1].replies.size @@ -136,7 +136,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_multiple_stis_and_order - author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: [Arel.sql("authors.name"), Arel.sql("comments.body"), Arel.sql("very_special_comments_posts.body")], where: "posts.id = 4").first + author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: ["authors.name", Arel.sql("comments.body"), Arel.sql("very_special_comments_posts.body")], where: "posts.id = 4").first assert_equal authors(:david), author assert_no_queries do author.posts.first.special_comments @@ -154,7 +154,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_where_first_level_returns_nil - authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: Arel.sql("authors.id DESC")).to_a + authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: "authors.id DESC").to_a assert_equal [authors(:bob), authors(:mary), authors(:david)], authors assert_no_queries do authors[2].post_about_thinking.comments.first @@ -162,17 +162,17 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through - source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: Arel.sql("vertices.id")).first + source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: "vertices.id").first assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first } end def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many - sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: Arel.sql("vertices.id DESC")).first + sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: "vertices.id DESC").first assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first } end def test_eager_association_loading_with_cascaded_interdependent_one_level_and_two_levels - authors_relation = Author.all.merge!(includes: [:comments, { posts: :categorizations }], order: Arel.sql("authors.id")) + authors_relation = Author.all.merge!(includes: [:comments, { posts: :categorizations }], order: "authors.id") authors = authors_relation.to_a assert_equal 3, authors.size assert_equal 10, authors[0].comments.size diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index d5ca87900e..75f851fec7 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -82,7 +82,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_with_ordering - list = Post.all.merge!(includes: :comments, order: Arel.sql("posts.id DESC")).to_a + list = Post.all.merge!(includes: :comments, order: "posts.id DESC").to_a [:other_by_mary, :other_by_bob, :misc_by_mary, :misc_by_bob, :eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments, :authorless, :thinking, :welcome ].each_with_index do |post, index| @@ -108,12 +108,12 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_with_two_tables_in_from_without_getting_double_quoted - posts = Post.select("posts.*").from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").order(Arel.sql("posts.id")).to_a + posts = Post.select("posts.*").from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").order("posts.id").to_a assert_equal 2, posts.first.comments.size end def test_loading_with_multiple_associations - posts = Post.all.merge!(includes: [ :comments, :author, :categories ], order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(includes: [ :comments, :author, :categories ], order: "posts.id").to_a assert_equal 2, posts.first.comments.size assert_equal 2, posts.first.categories.size assert_includes posts.first.comments, comments(:greetings) @@ -279,7 +279,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_loading_from_an_association - posts = authors(:david).posts.merge(includes: :comments, order: Arel.sql("posts.id")).to_a + posts = authors(:david).posts.merge(includes: :comments, order: "posts.id").to_a assert_equal 2, posts.first.comments.size end @@ -312,7 +312,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_nested_loading_through_has_one_association_with_order - aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: Arel.sql("author_addresses.id")).find(author_addresses(:david_address).id) + aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "author_addresses.id").find(author_addresses(:david_address).id) assert_equal aa.author.posts.count, aa.author.posts.length end @@ -364,31 +364,31 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_association_loading_with_belongs_to_and_limit - comments = Comment.all.merge!(includes: :post, limit: 5, order: Arel.sql("comments.id")).to_a + comments = Comment.all.merge!(includes: :post, limit: 5, order: "comments.id").to_a assert_equal 5, comments.length assert_equal [1, 2, 3, 5, 6], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_conditions - comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, order: Arel.sql("comments.id")).to_a + comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, order: "comments.id").to_a assert_equal 3, comments.length assert_equal [5, 6, 7], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset - comments = Comment.all.merge!(includes: :post, limit: 3, offset: 2, order: Arel.sql("comments.id")).to_a + comments = Comment.all.merge!(includes: :post, limit: 3, offset: 2, order: "comments.id").to_a assert_equal 3, comments.length assert_equal [3, 5, 6], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions - comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, offset: 1, order: Arel.sql("comments.id")).to_a + comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, offset: 1, order: "comments.id").to_a assert_equal 3, comments.length assert_equal [6, 7, 8], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array - comments = Comment.all.merge!(includes: :post, where: ["post_id = ?", 4], limit: 3, offset: 1, order: Arel.sql("comments.id")).to_a + comments = Comment.all.merge!(includes: :post, where: ["post_id = ?", 4], limit: 3, offset: 1, order: "comments.id").to_a assert_equal 3, comments.length assert_equal [6, 7, 8], comments.collect(&:id) end @@ -402,7 +402,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_conditions_hash comments = [] assert_nothing_raised do - comments = Comment.all.merge!(includes: :post, where: { posts: { id: 4 } }, limit: 3, order: Arel.sql("comments.id")).to_a + comments = Comment.all.merge!(includes: :post, where: { posts: { id: 4 } }, limit: 3, order: "comments.id").to_a end assert_equal 3, comments.length assert_equal [5, 6, 7], comments.collect(&:id) @@ -432,13 +432,13 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations - posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, order: "posts.id").to_a assert_equal 1, posts.length assert_equal [1], posts.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations - posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, offset: 1, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, offset: 1, order: "posts.id").to_a assert_equal 1, posts.length assert_equal [2], posts.collect(&:id) end @@ -508,9 +508,9 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_through - posts_with_comments = people(:michael).posts.merge(includes: :comments, order: Arel.sql("posts.id")).to_a - posts_with_author = people(:michael).posts.merge(includes: :author, order: Arel.sql("posts.id")).to_a - posts_with_comments_and_author = people(:michael).posts.merge(includes: [ :comments, :author ], order: Arel.sql("posts.id")).to_a + posts_with_comments = people(:michael).posts.merge(includes: :comments, order: "posts.id").to_a + posts_with_author = people(:michael).posts.merge(includes: :author, order: "posts.id").to_a + posts_with_comments_and_author = people(:michael).posts.merge(includes: [ :comments, :author ], order: "posts.id").to_a assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum + post.comments.size } assert_equal authors(:david), assert_no_queries { posts_with_author.first.author } assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author } @@ -526,7 +526,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_through_an_sti_join_model - author = Author.all.merge!(includes: :special_post_comments, order: Arel.sql("authors.id")).first + author = Author.all.merge!(includes: :special_post_comments, order: "authors.id").first assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments } end @@ -539,14 +539,14 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both - author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: Arel.sql("authors.id")).first + author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: "authors.id").first assert_equal [], author.special_nonexistent_post_comments end def test_eager_with_has_many_through_join_model_with_conditions assert_equal Author.all.merge!(includes: :hello_post_comments, - order: Arel.sql("authors.id")).first.hello_post_comments.sort_by(&:id), - Author.all.merge!(order: Arel.sql("authors.id")).first.hello_post_comments.sort_by(&:id) + order: "authors.id").first.hello_post_comments.sort_by(&:id), + Author.all.merge!(order: "authors.id").first.hello_post_comments.sort_by(&:id) end def test_eager_with_has_many_through_join_model_with_conditions_on_top_level @@ -573,19 +573,19 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_and_limit - posts = Post.all.merge!(order: Arel.sql("posts.id asc"), includes: [ :author, :comments ], limit: 2).to_a + posts = Post.all.merge!(order: "posts.id asc", includes: [ :author, :comments ], limit: 2).to_a assert_equal 2, posts.size assert_equal 3, posts.inject(0) { |sum, post| sum + post.comments.size } end def test_eager_with_has_many_and_limit_and_conditions - posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.body = 'hello'", order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.body = 'hello'", order: "posts.id").to_a assert_equal 2, posts.size assert_equal [4, 5], posts.collect(&:id) end def test_eager_with_has_many_and_limit_and_conditions_array - posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: [ "posts.body = ?", "hello" ], order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: [ "posts.body = ?", "hello" ], order: "posts.id").to_a assert_equal 2, posts.size assert_equal [4, 5], posts.collect(&:id) end @@ -643,7 +643,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_and_belongs_to_many_and_limit - posts = Post.all.merge!(includes: :categories, order: Arel.sql("posts.id"), limit: 3).to_a + posts = Post.all.merge!(includes: :categories, order: "posts.id", limit: 3).to_a assert_equal 3, posts.size assert_equal 2, posts[0].categories.size assert_equal 1, posts[1].categories.size @@ -709,7 +709,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_association_loading_with_habtm - posts = Post.all.merge!(includes: :categories, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(includes: :categories, order: "posts.id").to_a assert_equal 2, posts[0].categories.size assert_equal 1, posts[1].categories.size assert_equal 0, posts[2].categories.size @@ -891,14 +891,14 @@ class EagerAssociationTest < ActiveRecord::TestCase posts(:thinking, :sti_comments), Post.all.merge!( includes: [:author, :comments], where: { "authors.name" => "David" }, - order: [Arel.sql("UPPER(posts.title)"), Arel.sql("posts.id")], limit: 2, offset: 1 + order: [Arel.sql("UPPER(posts.title)"), "posts.id"], limit: 2, offset: 1 ).to_a ) assert_equal( posts(:sti_post_and_comments, :sti_comments), Post.all.merge!( includes: [:author, :comments], where: { "authors.name" => "David" }, - order: [Arel.sql("UPPER(posts.title) DESC"), Arel.sql("posts.id")], limit: 2, offset: 1 + order: [Arel.sql("UPPER(posts.title) DESC"), "posts.id"], limit: 2, offset: 1 ).to_a ) end @@ -909,7 +909,7 @@ class EagerAssociationTest < ActiveRecord::TestCase Person.references(:number1_fans_people).merge( includes: [:readers, :primary_contact, :number1_fan], where: "number1_fans_people.first_name like 'M%'", - order: Arel.sql("people.id"), limit: 2, offset: 0 + order: "people.id", limit: 2, offset: 0 ).to_a ) end @@ -1102,18 +1102,18 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_loading_with_conditions_on_joined_table_preloads posts = assert_queries(2) do - Post.all.merge!(select: "distinct posts.*", includes: :author, joins: [:comments], where: "comments.body like 'Thank you%'", order: Arel.sql("posts.id")).to_a + Post.all.merge!(select: "distinct posts.*", includes: :author, joins: [:comments], where: "comments.body like 'Thank you%'", order: "posts.id").to_a end assert_equal [posts(:welcome)], posts assert_equal authors(:david), assert_no_queries { posts[0].author } posts = assert_queries(2) do - Post.all.merge!(includes: :author, joins: { taggings: :tag }, where: "tags.name = 'General'", order: Arel.sql("posts.id")).to_a + Post.all.merge!(includes: :author, joins: { taggings: :tag }, where: "tags.name = 'General'", order: "posts.id").to_a end assert_equal posts(:welcome, :thinking), posts posts = assert_queries(2) do - Post.all.merge!(includes: :author, joins: { taggings: { tag: :taggings } }, where: "taggings_tags.super_tag_id=2", order: Arel.sql("posts.id")).to_a + Post.all.merge!(includes: :author, joins: { taggings: { tag: :taggings } }, where: "taggings_tags.super_tag_id=2", order: "posts.id").to_a end assert_equal posts(:welcome, :thinking), posts end @@ -1132,13 +1132,13 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_loading_with_conditions_on_string_joined_table_preloads posts = assert_queries(2) do - Post.all.merge!(select: "distinct posts.*", includes: :author, joins: "INNER JOIN comments on comments.post_id = posts.id", where: "comments.body like 'Thank you%'", order: Arel.sql("posts.id")).to_a + Post.all.merge!(select: "distinct posts.*", includes: :author, joins: "INNER JOIN comments on comments.post_id = posts.id", where: "comments.body like 'Thank you%'", order: "posts.id").to_a end assert_equal [posts(:welcome)], posts assert_equal authors(:david), assert_no_queries { posts[0].author } posts = assert_queries(2) do - Post.all.merge!(select: "distinct posts.*", includes: :author, joins: ["INNER JOIN comments on comments.post_id = posts.id"], where: "comments.body like 'Thank you%'", order: Arel.sql("posts.id")).to_a + Post.all.merge!(select: "distinct posts.*", includes: :author, joins: ["INNER JOIN comments on comments.post_id = posts.id"], where: "comments.body like 'Thank you%'", order: "posts.id").to_a end assert_equal [posts(:welcome)], posts assert_equal authors(:david), assert_no_queries { posts[0].author } @@ -1146,7 +1146,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_loading_with_select_on_joined_table_preloads posts = assert_queries(2) do - Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: Arel.sql("posts.id")).to_a + Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: "posts.id").to_a end assert_equal "David", posts[0].author_name assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments } @@ -1199,7 +1199,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_preload_has_one_using_primary_key expected = accounts(:signals37) - firm = Firm.all.merge!(includes: :account_using_primary_key, order: Arel.sql("companies.id")).first + firm = Firm.all.merge!(includes: :account_using_primary_key, order: "companies.id").first assert_no_queries do assert_equal expected, firm.account_using_primary_key end @@ -1269,7 +1269,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_joins_with_includes_should_preload_via_joins - post = assert_queries(1) { Post.includes(:comments).joins(:comments).order(Arel.sql("posts.id desc")).to_a.first } + post = assert_queries(1) { Post.includes(:comments).joins(:comments).order("posts.id desc").to_a.first } assert_queries(0) do assert_not_equal 0, post.comments.to_a.count @@ -1284,10 +1284,10 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_deep_including_through_habtm # warm up habtm cache - posts = Post.all.merge!(includes: { categories: :categorizations }, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(includes: { categories: :categorizations }, order: "posts.id").to_a posts[0].categories[0].categorizations.length - posts = Post.all.merge!(includes: { categories: :categorizations }, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(includes: { categories: :categorizations }, order: "posts.id").to_a assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length } assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length } assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length } @@ -1513,6 +1513,6 @@ class EagerAssociationTest < ActiveRecord::TestCase private def find_all_ordered(klass, include = nil) - klass.order(Arel.sql("#{klass.table_name}.#{klass.primary_key}")).includes(include).to_a + klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 85733f9056..3597da7ff3 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -48,7 +48,7 @@ class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCa author = authors(:david) # this can fail on adapters which require ORDER BY expressions to be included in the SELECT expression # if the reorder clauses are not correctly handled - assert author.posts_with_comments_sorted_by_comment_id.where("comments.id > 0").reorder(Arel.sql("posts.comments_count DESC"), Arel.sql("posts.tags_count DESC")).last + assert author.posts_with_comments_sorted_by_comment_id.where("comments.id > 0").reorder("posts.comments_count DESC", "posts.tags_count DESC").last end end @@ -1123,7 +1123,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_deleting_updates_counter_cache - topic = Topic.order(Arel.sql("id ASC")).first + topic = Topic.order("id ASC").first assert_equal topic.replies.to_a.size, topic.replies_count topic.replies.delete(topic.replies.first) @@ -1162,7 +1162,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_pushing_association_updates_counter_cache - topic = Topic.order(Arel.sql("id ASC")).first + topic = Topic.order("id ASC").first reply = Reply.create! assert_difference "topic.reload.replies_count", 1 do @@ -1212,7 +1212,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_calling_update_attributes_on_id_changes_the_counter_cache - topic = Topic.order(Arel.sql("id ASC")).first + topic = Topic.order("id ASC").first original_count = topic.replies.to_a.size assert_equal original_count, topic.replies_count 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 ed96eb54c1..046020e310 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -71,7 +71,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def self.name; "Person"; end has_many :readers - has_many :posts, -> { order(Arel.sql("posts.id DESC")) }, through: :readers + has_many :posts, -> { order("posts.id DESC") }, through: :readers end posts = person_prime.includes(:posts).first.posts @@ -985,7 +985,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_joining_has_many_through_belongs_to - posts = Post.joins(:author_categorizations).order(Arel.sql("posts.id")). + posts = Post.joins(:author_categorizations).order("posts.id"). where("categorizations.id" => categorizations(:mary_thinking_sti).id) assert_equal [posts(:eager_other), posts(:misc_by_mary), posts(:other_by_mary)], posts diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 8f08684820..a3acb1a152 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -244,8 +244,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_include_has_many_through - posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a - posts_with_authors = Post.all.merge!(includes: :authors, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(order: "posts.id").to_a + posts_with_authors = Post.all.merge!(includes: :authors, order: "posts.id").to_a assert_equal posts.length, posts_with_authors.length posts.length.times do |i| assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length } @@ -269,8 +269,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_include_polymorphic_has_many_through - posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a - posts_with_tags = Post.all.merge!(includes: :tags, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(order: "posts.id").to_a + posts_with_tags = Post.all.merge!(includes: :tags, order: "posts.id").to_a assert_equal posts.length, posts_with_tags.length posts.length.times do |i| assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length } @@ -278,8 +278,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_include_polymorphic_has_many - posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a - posts_with_taggings = Post.all.merge!(includes: :taggings, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(order: "posts.id").to_a + posts_with_taggings = Post.all.merge!(includes: :taggings, order: "posts.id").to_a assert_equal posts.length, posts_with_taggings.length posts.length.times do |i| assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length } @@ -326,7 +326,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_through_with_custom_primary_key_on_has_many_source - assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order(Arel.sql("authors.id")) + assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order("authors.id") end def test_belongs_to_polymorphic_with_counter_cache @@ -383,19 +383,19 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_through_has_many_find_all - assert_equal comments(:greetings), authors(:david).comments.order(Arel.sql("comments.id")).to_a.first + assert_equal comments(:greetings), authors(:david).comments.order("comments.id").to_a.first end def test_has_many_through_has_many_find_all_with_custom_class - assert_equal comments(:greetings), authors(:david).funky_comments.order(Arel.sql("comments.id")).to_a.first + assert_equal comments(:greetings), authors(:david).funky_comments.order("comments.id").to_a.first end def test_has_many_through_has_many_find_first - assert_equal comments(:greetings), authors(:david).comments.order(Arel.sql("comments.id")).first + assert_equal comments(:greetings), authors(:david).comments.order("comments.id").first end def test_has_many_through_has_many_find_conditions - options = { where: "comments.#{QUOTED_TYPE}='SpecialComment'", order: Arel.sql("comments.id") } + options = { where: "comments.#{QUOTED_TYPE}='SpecialComment'", order: "comments.id" } assert_equal comments(:does_it_hurt), authors(:david).comments.merge(options).first end @@ -661,8 +661,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_preload_polymorphic_has_many_through - posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a - posts_with_tags = Post.all.merge!(includes: :tags, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(order: "posts.id").to_a + posts_with_tags = Post.all.merge!(includes: :tags, order: "posts.id").to_a assert_equal posts.length, posts_with_tags.length posts.length.times do |i| assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length } @@ -688,8 +688,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_preload_polymorphic_has_many - posts = Post.all.merge!(order: Arel.sql("posts.id")).to_a - posts_with_taggings = Post.all.merge!(includes: :taggings, order: Arel.sql("posts.id")).to_a + posts = Post.all.merge!(order: "posts.id").to_a + posts_with_taggings = Post.all.merge!(includes: :taggings, order: "posts.id").to_a assert_equal posts.length, posts_with_taggings.length posts.length.times do |i| assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length } diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index b8e8f48ae5..0254da9a99 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -86,7 +86,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase # Through: has_many through def test_has_many_through_has_many_through_with_has_many_source_reflection luke, david = subscribers(:first), subscribers(:second) - assert_equal [luke, david, david], authors(:david).subscribers.order(Arel.sql("subscribers.nick")) + assert_equal [luke, david, david], authors(:david).subscribers.order("subscribers.nick") end def test_has_many_through_has_many_through_with_has_many_source_reflection_preload @@ -156,7 +156,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) assert_equal [groucho_details, other_details], - members(:groucho).organization_member_details.order(Arel.sql("member_details.id")) + members(:groucho).organization_member_details.order("member_details.id") end def test_has_many_through_has_one_with_has_many_through_source_reflection_preload @@ -187,7 +187,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy) assert_equal [groucho_details, other_details], - members(:groucho).organization_member_details_2.order(Arel.sql("member_details.id")) + members(:groucho).organization_member_details_2.order("member_details.id") end def test_has_many_through_has_one_through_with_has_many_source_reflection_preload @@ -218,7 +218,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection general, cooking = categories(:general), categories(:cooking) - assert_equal [general, cooking], authors(:bob).post_categories.order(Arel.sql("categories.id")) + assert_equal [general, cooking], authors(:bob).post_categories.order("categories.id") end def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload @@ -246,7 +246,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection greetings, more = comments(:greetings), comments(:more_greetings) - assert_equal [greetings, more], categories(:technology).post_comments.order(Arel.sql("comments.id")) + assert_equal [greetings, more], categories(:technology).post_comments.order("comments.id") end def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload @@ -264,7 +264,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase Category.joins(:post_comments).first assert_includes_and_joins_equal( - Category.where("comments.id" => comments(:more_greetings).id).order(Arel.sql("categories.id")), + Category.where("comments.id" => comments(:more_greetings).id).order("categories.id"), [categories(:general), categories(:technology)], :post_comments ) end @@ -275,7 +275,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection greetings, more = comments(:greetings), comments(:more_greetings) - assert_equal [greetings, more], authors(:bob).category_post_comments.order(Arel.sql("comments.id")) + assert_equal [greetings, more], authors(:bob).category_post_comments.order("comments.id") end def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload @@ -292,7 +292,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase Author.joins(:category_post_comments).first assert_includes_and_joins_equal( - Author.where("comments.id" => comments(:does_it_hurt).id).order(Arel.sql("authors.id")), + Author.where("comments.id" => comments(:does_it_hurt).id).order("authors.id"), [authors(:david), authors(:mary)], :category_post_comments ) end @@ -327,7 +327,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase welcome_general, thinking_general = taggings(:welcome_general), taggings(:thinking_general) assert_equal [welcome_general, thinking_general], - categorizations(:david_welcome_general).post_taggings.order(Arel.sql("taggings.id")) + categorizations(:david_welcome_general).post_taggings.order("taggings.id") end def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload @@ -411,7 +411,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_distinct_has_many_through_a_has_many_through_association_on_through_reflection author = authors(:david) assert_equal [subscribers(:first), subscribers(:second)], - author.distinct_subscribers.order(Arel.sql("subscribers.nick")) + author.distinct_subscribers.order("subscribers.nick") end def test_nested_has_many_through_with_a_table_referenced_multiple_times @@ -436,7 +436,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase end def test_has_many_through_with_foreign_key_option_on_through_reflection - assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order(Arel.sql("posts.id")) + assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order("posts.id") assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors references = Reference.joins(:agents_posts_authors).where("authors.id" => authors(:david).id) @@ -444,7 +444,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase end def test_has_many_through_with_foreign_key_option_on_source_reflection - assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order(Arel.sql("people.id")) + assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order("people.id") jobs = Job.joins(:agents) assert_equal [jobs(:unicyclist), jobs(:unicyclist)], jobs diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index e2e4aa22f4..a45b3a1644 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -439,7 +439,7 @@ class BasicsTest < ActiveRecord::TestCase if current_adapter?(:Mysql2Adapter) def test_update_all_with_order_and_limit - assert_equal 1, Topic.limit(1).order(Arel.sql("id DESC")).update_all(content: "bulk updated!") + assert_equal 1, Topic.limit(1).order("id DESC").update_all(content: "bulk updated!") end end @@ -1081,11 +1081,11 @@ class BasicsTest < ActiveRecord::TestCase def test_find_last last = Developer.last - assert_equal last, Developer.all.merge!(order: Arel.sql("id desc")).first + assert_equal last, Developer.all.merge!(order: "id desc").first end def test_last - assert_equal Developer.all.merge!(order: Arel.sql("id desc")).first, Developer.last + assert_equal Developer.all.merge!(order: "id desc").first, Developer.last end def test_all @@ -1095,17 +1095,17 @@ class BasicsTest < ActiveRecord::TestCase end def test_all_with_conditions - assert_equal Developer.all.merge!(order: Arel.sql("id desc")).to_a, Developer.order(Arel.sql("id desc")).to_a + assert_equal Developer.all.merge!(order: "id desc").to_a, Developer.order("id desc").to_a end def test_find_ordered_last - last = Developer.all.merge!(order: Arel.sql("developers.salary ASC")).last - assert_equal last, Developer.all.merge!(order: Arel.sql("developers.salary ASC")).to_a.last + last = Developer.all.merge!(order: "developers.salary ASC").last + assert_equal last, Developer.all.merge!(order: "developers.salary ASC").to_a.last end def test_find_reverse_ordered_last - last = Developer.all.merge!(order: Arel.sql("developers.salary DESC")).last - assert_equal last, Developer.all.merge!(order: Arel.sql("developers.salary DESC")).to_a.last + last = Developer.all.merge!(order: "developers.salary DESC").last + assert_equal last, Developer.all.merge!(order: "developers.salary DESC").to_a.last end def test_find_multiple_ordered_last @@ -1115,7 +1115,7 @@ class BasicsTest < ActiveRecord::TestCase def test_find_keeps_multiple_order_values combined = Developer.all.merge!(order: Arel.sql("developers.name, developers.salary")).to_a - assert_equal combined, Developer.all.merge!(order: [Arel.sql("developers.name"), Arel.sql("developers.salary")]).to_a + assert_equal combined, Developer.all.merge!(order: ["developers.name", "developers.salary"]).to_a end def test_find_keeps_multiple_group_values diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index ff345d5f0e..be8aeed5ac 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -9,7 +9,7 @@ class EachTest < ActiveRecord::TestCase fixtures :posts, :subscribers def setup - @posts = Post.order(Arel.sql("id asc")) + @posts = Post.order("id asc") @total = Post.count Post.count("id") # preheat arel's table cache end @@ -101,7 +101,7 @@ class EachTest < ActiveRecord::TestCase previous_logger = ActiveRecord::Base.logger ActiveRecord::Base.logger = nil assert_nothing_raised do - Post.order(Arel.sql("comments_count DESC")).find_each { |post| post } + Post.order("comments_count DESC").find_each { |post| post } end ensure ActiveRecord::Base.logger = previous_logger @@ -233,7 +233,7 @@ class EachTest < ActiveRecord::TestCase end def test_find_in_batches_should_use_any_column_as_primary_key - nick_order_subscribers = Subscriber.order(Arel.sql("nick asc")) + nick_order_subscribers = Subscriber.order("nick asc") start_nick = nick_order_subscribers.second.nick subscribers = [] @@ -329,7 +329,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_each_record_should_be_ordered_by_id - ids = Post.order(Arel.sql("id ASC")).pluck(:id) + ids = Post.order("id ASC").pluck(:id) assert_queries(6) do Post.in_batches(of: 2).each_record.with_index do |post, i| assert_equal ids[i], post.id @@ -384,7 +384,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_should_start_from_the_start_option - post = Post.order(Arel.sql("id ASC")).where("id >= ?", 2).first + post = Post.order("id ASC").where("id >= ?", 2).first assert_queries(2) do relation = Post.in_batches(of: 1, start: 2).first assert_equal post, relation.first @@ -392,7 +392,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_should_end_at_the_finish_option - post = Post.order(Arel.sql("id DESC")).where("id <= ?", 5).first + post = Post.order("id DESC").where("id <= ?", 5).first assert_queries(7) do relation = Post.in_batches(of: 1, finish: 5, load: true).reverse_each.first assert_equal post, relation.last @@ -451,7 +451,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_should_use_any_column_as_primary_key - nick_order_subscribers = Subscriber.order(Arel.sql("nick asc")) + nick_order_subscribers = Subscriber.order("nick asc") start_nick = nick_order_subscribers.second.nick subscribers = [] diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 65ebfcd989..344d1a6639 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -813,9 +813,9 @@ class FinderTest < ActiveRecord::TestCase end def test_hash_condition_find_with_array - p1, p2 = Post.limit(2).order(Arel.sql("id asc")).to_a - assert_equal [p1, p2], Post.where(id: [p1, p2]).order(Arel.sql("id asc")).to_a - assert_equal [p1, p2], Post.where(id: [p1, p2.id]).order(Arel.sql("id asc")).to_a + p1, p2 = Post.limit(2).order("id asc").to_a + assert_equal [p1, p2], Post.where(id: [p1, p2]).order("id asc").to_a + assert_equal [p1, p2], Post.where(id: [p1, p2.id]).order("id asc").to_a end def test_hash_condition_find_with_nil @@ -1013,7 +1013,7 @@ class FinderTest < ActiveRecord::TestCase end def test_find_by_one_attribute_with_several_options - assert_equal accounts(:unknown), Account.order(Arel.sql("id DESC")).where("id != ?", 3).find_by_credit_limit(50) + assert_equal accounts(:unknown), Account.order("id DESC").where("id != ?", 3).find_by_credit_limit(50) end def test_find_by_one_missing_attribute @@ -1041,7 +1041,7 @@ class FinderTest < ActiveRecord::TestCase assert_equal devs[2], Developer.offset(2).first assert_equal devs[-3], Developer.offset(2).last - assert_equal devs[-3], Developer.offset(2).order(Arel.sql("id DESC")).first + assert_equal devs[-3], Developer.offset(2).order("id DESC").first end def test_find_by_nil_attribute @@ -1094,9 +1094,9 @@ class FinderTest < ActiveRecord::TestCase end def test_find_by_records - p1, p2 = Post.limit(2).order(Arel.sql("id asc")).to_a - assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2]]).order(Arel.sql("id asc")) - assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2.id]]).order(Arel.sql("id asc")) + p1, p2 = Post.limit(2).order("id asc").to_a + assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2]]).order("id asc") + assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2.id]]).order("id asc") end def test_select_value @@ -1136,7 +1136,7 @@ class FinderTest < ActiveRecord::TestCase client_of = Company. where(client_of: [2, 1, nil], name: ["37signals", "Summit", "Microsoft"]). - order(Arel.sql("client_of DESC")). + order("client_of DESC"). map(&:client_of) assert_includes client_of, nil @@ -1146,7 +1146,7 @@ class FinderTest < ActiveRecord::TestCase def test_find_with_nil_inside_set_passed_for_attribute client_of = Company. where(client_of: [nil]). - order(Arel.sql("client_of DESC")). + order("client_of DESC"). map(&:client_of) assert_equal [], client_of.compact @@ -1155,7 +1155,7 @@ class FinderTest < ActiveRecord::TestCase def test_with_limiting_with_custom_select posts = Post.references(:authors).merge( includes: :author, select: 'posts.*, authors.id as "author_id"', - limit: 3, order: Arel.sql("posts.id") + limit: 3, order: "posts.id" ).to_a assert_equal 3, posts.size assert_equal [0, 1, 1], posts.map(&:author_id).sort diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 3b35df526f..8a656d7720 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -13,10 +13,10 @@ class RelationMergingTest < ActiveRecord::TestCase fixtures :developers, :comments, :authors, :author_addresses, :posts def test_relation_merging - devs = Developer.where("salary >= 80000").merge(Developer.limit(2)).merge(Developer.order(Arel.sql("id ASC")).where("id < 3")) + devs = Developer.where("salary >= 80000").merge(Developer.limit(2)).merge(Developer.order("id ASC").where("id < 3")) assert_equal [developers(:david), developers(:jamis)], devs.to_a - dev_with_count = Developer.limit(1).merge(Developer.order(Arel.sql("id DESC"))).merge(Developer.select("developers.*")) + dev_with_count = Developer.limit(1).merge(Developer.order("id DESC")).merge(Developer.select("developers.*")) assert_equal [developers(:poor_jamis)], dev_with_count.to_a end @@ -57,7 +57,7 @@ class RelationMergingTest < ActiveRecord::TestCase end def test_relation_merging_with_locks - devs = Developer.lock.where("salary >= 80000").order(Arel.sql("id DESC")).merge(Developer.limit(2)) + devs = Developer.lock.where("salary >= 80000").order("id DESC").merge(Developer.limit(2)) assert devs.locked? end @@ -118,7 +118,7 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase test "merging where relations" do hello_by_bob = Post.where(body: "hello").joins(:author). - merge(Author.where(name: "Bob")).order(Arel.sql("posts.id")).pluck(Arel.sql("posts.id")) + merge(Author.where(name: "Bob")).order("posts.id").pluck("posts.id") assert_equal [posts(:misc_by_bob).id, posts(:other_by_bob).id], hello_by_bob diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb index 2abc3b7fe8..7e418f9c7d 100644 --- a/activerecord/test/cases/relation/or_test.rb +++ b/activerecord/test/cases/relation/or_test.rb @@ -50,15 +50,15 @@ module ActiveRecord end def test_or_preserves_other_querying_methods - expected = Post.where("id = 1 or id = 2 or id = 3").order(Arel.sql("body asc")).to_a - partial = Post.order(Arel.sql("body asc")) + expected = Post.where("id = 1 or id = 2 or id = 3").order("body asc").to_a + partial = Post.order("body asc") assert_equal expected, partial.where("id = 1").or(partial.where(id: [2, 3])).to_a - assert_equal expected, Post.order(Arel.sql("body asc")).where("id = 1").or(Post.order(Arel.sql("body asc")).where(id: [2, 3])).to_a + assert_equal expected, Post.order("body asc").where("id = 1").or(Post.order("body asc").where(id: [2, 3])).to_a end def test_or_with_incompatible_relations error = assert_raises ArgumentError do - Post.order(Arel.sql("body asc")).where("id = 1").or(Post.order(Arel.sql("id desc")).where(id: [2, 3])).to_a + Post.order("body asc").where("id = 1").or(Post.order("id desc").where(id: [2, 3])).to_a end assert_equal "Relation passed to #or must be structurally compatible. Incompatible values: [:order]", error.message @@ -78,12 +78,12 @@ module ActiveRecord def test_or_with_unscope_order expected = Post.where("id = 1 or id = 2") - assert_equal expected, Post.order(Arel.sql("body asc")).where("id = 1").unscope(:order).or(Post.where("id = 2")).to_a + assert_equal expected, Post.order("body asc").where("id = 1").unscope(:order).or(Post.where("id = 2")).to_a end def test_or_with_incompatible_unscope error = assert_raises ArgumentError do - Post.order(Arel.sql("body asc")).where("id = 1").or(Post.order(Arel.sql("body asc")).where("id = 2").unscope(:order)).to_a + Post.order("body asc").where("id = 1").or(Post.order("body asc").where("id = 2").unscope(:order)).to_a end assert_equal "Relation passed to #or must be structurally compatible. Incompatible values: [:order]", error.message @@ -101,7 +101,7 @@ module ActiveRecord end def test_or_inside_named_scope - expected = Post.where("body LIKE '\%a\%' OR title LIKE ?", "%'%").order(Arel.sql("id DESC")).to_a + expected = Post.where("body LIKE '\%a\%' OR title LIKE ?", "%'%").order("id DESC").to_a assert_equal expected, Post.order(id: :desc).typographically_interesting end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 906f3499dd..ab2e432e78 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -102,7 +102,7 @@ class RelationTest < ActiveRecord::TestCase end def test_scoped_first - topics = Topic.all.order(Arel.sql("id ASC")) + topics = Topic.all.order("id ASC") assert_queries(1) do 2.times { assert_equal "The First Topic", topics.first.title } @@ -112,7 +112,7 @@ class RelationTest < ActiveRecord::TestCase end def test_loaded_first - topics = Topic.all.order(Arel.sql("id ASC")) + topics = Topic.all.order("id ASC") topics.load # force load assert_no_queries do @@ -123,7 +123,7 @@ class RelationTest < ActiveRecord::TestCase end def test_loaded_first_with_limit - topics = Topic.all.order(Arel.sql("id ASC")) + topics = Topic.all.order("id ASC") topics.load # force load assert_no_queries do @@ -135,7 +135,7 @@ class RelationTest < ActiveRecord::TestCase end def test_first_get_more_than_available - topics = Topic.all.order(Arel.sql("id ASC")) + topics = Topic.all.order("id ASC") unloaded_first = topics.first(10) topics.load # force load @@ -220,7 +220,7 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_arel_assoc_order - topics = Topic.order(Arel.sql("id") => :desc) + topics = Topic.order("id" => :desc) assert_equal 5, topics.to_a.size assert_equal topics(:fifth).title, topics.first.title end @@ -232,7 +232,7 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_reversed_arel_assoc_order - topics = Topic.order(Arel.sql("id") => :asc).reverse_order + topics = Topic.order("id" => :asc).reverse_order assert_equal 5, topics.to_a.size assert_equal topics(:fifth).title, topics.first.title end @@ -319,7 +319,7 @@ class RelationTest < ActiveRecord::TestCase end def test_raising_exception_on_invalid_hash_params - e = assert_raise(ArgumentError) { Topic.order(Arel.sql("name"), Arel.sql("id DESC"), id: :asfsdf) } + e = assert_raise(ArgumentError) { Topic.order(Arel.sql("name"), "id DESC", id: :asfsdf) } assert_equal 'Direction "asfsdf" is invalid. Valid directions are: [:asc, :desc, :ASC, :DESC, "asc", "desc", "ASC", "DESC"]', e.message end @@ -365,7 +365,7 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_order_and_take - entrants = Entrant.order(Arel.sql("id ASC")).limit(2).to_a + entrants = Entrant.order("id ASC").limit(2).to_a assert_equal 2, entrants.size assert_equal entrants(:first).name, entrants.first.name @@ -374,7 +374,7 @@ class RelationTest < ActiveRecord::TestCase def test_finding_with_cross_table_order_and_limit tags = Tag.includes(:taggings). order( - Arel.sql("tags.name asc"), + "tags.name asc", Arel.sql("taggings.taggable_id asc"), Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)") ).limit(1).to_a @@ -403,12 +403,12 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_order_limit_and_offset - entrants = Entrant.order(Arel.sql("id ASC")).limit(2).offset(1) + entrants = Entrant.order("id ASC").limit(2).offset(1) assert_equal 2, entrants.to_a.size assert_equal entrants(:second).name, entrants.first.name - entrants = Entrant.order(Arel.sql("id ASC")).limit(2).offset(2) + entrants = Entrant.order("id ASC").limit(2).offset(2) assert_equal 1, entrants.to_a.size assert_equal entrants(:third).name, entrants.first.name end @@ -518,27 +518,27 @@ class RelationTest < ActiveRecord::TestCase def test_find_with_preloaded_associations assert_queries(2) do - posts = Post.preload(:comments).order(Arel.sql("posts.id")) + posts = Post.preload(:comments).order("posts.id") assert posts.first.comments.first end assert_queries(2) do - posts = Post.preload(:comments).order(Arel.sql("posts.id")) + posts = Post.preload(:comments).order("posts.id") assert posts.first.comments.first end assert_queries(2) do - posts = Post.preload(:author).order(Arel.sql("posts.id")) + posts = Post.preload(:author).order("posts.id") assert posts.first.author end assert_queries(2) do - posts = Post.preload(:author).order(Arel.sql("posts.id")) + posts = Post.preload(:author).order("posts.id") assert posts.first.author end assert_queries(3) do - posts = Post.preload(:author, :comments).order(Arel.sql("posts.id")) + posts = Post.preload(:author, :comments).order("posts.id") assert posts.first.author assert posts.first.comments.first end @@ -553,22 +553,22 @@ class RelationTest < ActiveRecord::TestCase def test_find_with_included_associations assert_queries(2) do - posts = Post.includes(:comments).order(Arel.sql("posts.id")) + posts = Post.includes(:comments).order("posts.id") assert posts.first.comments.first end assert_queries(2) do - posts = Post.all.includes(:comments).order(Arel.sql("posts.id")) + posts = Post.all.includes(:comments).order("posts.id") assert posts.first.comments.first end assert_queries(2) do - posts = Post.includes(:author).order(Arel.sql("posts.id")) + posts = Post.includes(:author).order("posts.id") assert posts.first.author end assert_queries(3) do - posts = Post.includes(:author, :comments).order(Arel.sql("posts.id")) + posts = Post.includes(:author, :comments).order("posts.id") assert posts.first.author assert posts.first.comments.first end @@ -698,7 +698,7 @@ class RelationTest < ActiveRecord::TestCase end def test_find_ids - authors = Author.order(Arel.sql("id ASC")) + authors = Author.order("id ASC") results = authors.find(authors(:david).id, authors(:mary).id) assert_kind_of Array, results @@ -982,7 +982,7 @@ class RelationTest < ActiveRecord::TestCase end def test_multiple_selects - post = Post.all.select("comments_count").select("title").order(Arel.sql("id ASC")).first + post = Post.all.select("comments_count").select("title").order("id ASC").first assert_equal "Welcome to the weblog", post.title assert_equal 2, post.comments_count end @@ -1344,7 +1344,7 @@ class RelationTest < ActiveRecord::TestCase end def test_except - relation = Post.where(author_id: 1).order(Arel.sql("id ASC")).limit(1) + relation = Post.where(author_id: 1).order("id ASC").limit(1) assert_equal [posts(:welcome)], relation.to_a author_posts = relation.except(:order, :limit) @@ -1355,7 +1355,7 @@ class RelationTest < ActiveRecord::TestCase end def test_only - relation = Post.where(author_id: 1).order(Arel.sql("id ASC")).limit(1) + relation = Post.where(author_id: 1).order("id ASC").limit(1) assert_equal [posts(:welcome)], relation.to_a author_posts = relation.only(:where) @@ -1366,7 +1366,7 @@ class RelationTest < ActiveRecord::TestCase end def test_anonymous_extension - relation = Post.where(author_id: 1).order(Arel.sql("id ASC")).extending do + relation = Post.where(author_id: 1).order("id ASC").extending do def author "lifo" end @@ -1377,7 +1377,7 @@ class RelationTest < ActiveRecord::TestCase end def test_named_extension - relation = Post.where(author_id: 1).order(Arel.sql("id ASC")).extending(Post::NamedExtension) + relation = Post.where(author_id: 1).order("id ASC").extending(Post::NamedExtension) assert_equal "lifo", relation.author assert_equal "lifo", relation.limit(1).author end @@ -1392,13 +1392,13 @@ class RelationTest < ActiveRecord::TestCase end def test_order_using_scoping - car1 = CoolCar.order(Arel.sql("id DESC")).scoping do - CoolCar.all.merge!(order: Arel.sql("id asc")).first + car1 = CoolCar.order("id DESC").scoping do + CoolCar.all.merge!(order: "id asc").first end assert_equal "zyke", car1.name - car2 = FastCar.order(Arel.sql("id DESC")).scoping do - FastCar.all.merge!(order: Arel.sql("id asc")).first + car2 = FastCar.order("id DESC").scoping do + FastCar.all.merge!(order: "id asc").first end assert_equal "zyke", car2.name end @@ -1442,7 +1442,7 @@ class RelationTest < ActiveRecord::TestCase end def test_update_all_with_joins_and_limit_and_order - comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order(Arel.sql("comments.id")).limit(1) + comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("comments.id").limit(1) assert_equal 1, comments.update_all(post_id: posts(:thinking).id) assert_equal posts(:thinking), comments(:greetings).post assert_equal posts(:welcome), comments(:more_greetings).post @@ -1457,7 +1457,7 @@ class RelationTest < ActiveRecord::TestCase end def test_update_all_with_joins_and_offset_and_order - all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order(Arel.sql("posts.id"), Arel.sql("comments.id")) + all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order(Arel.sql("posts.id"), "comments.id") count = all_comments.count comments = all_comments.offset(1) @@ -1814,7 +1814,7 @@ class RelationTest < ActiveRecord::TestCase end test "joins with select" do - posts = Post.joins(:author).select("id", "authors.author_address_id").order(Arel.sql("posts.id")).limit(3) + posts = Post.joins(:author).select("id", "authors.author_address_id").order("posts.id").limit(3) assert_equal [1, 2, 4], posts.map(&:id) assert_equal [1, 1, 1], posts.map(&:author_address_id) end diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 26f0b86703..0d64db140c 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -13,7 +13,7 @@ class DefaultScopingTest < ActiveRecord::TestCase fixtures :developers, :posts, :comments def test_default_scope - expected = Developer.all.merge!(order: Arel.sql("salary DESC")).to_a.collect(&:salary) + expected = Developer.all.merge!(order: "salary DESC").to_a.collect(&:salary) received = DeveloperOrderedBySalary.all.collect(&:salary) assert_equal expected, received end @@ -86,14 +86,14 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_reorder_overrides_default_scope_order - expected = Developer.order(Arel.sql("name DESC")).collect(&:name) - received = DeveloperOrderedBySalary.reorder(Arel.sql("name DESC")).collect(&:name) + expected = Developer.order("name DESC").collect(&:name) + received = DeveloperOrderedBySalary.reorder("name DESC").collect(&:name) assert_equal expected, received end def test_order_after_reorder_combines_orders expected = Developer.order(Arel.sql("name DESC, id DESC")).collect { |dev| [dev.name, dev.id] } - received = Developer.order(Arel.sql("name ASC")).reorder(Arel.sql("name DESC")).order(Arel.sql("id DESC")).collect { |dev| [dev.name, dev.id] } + received = Developer.order("name ASC").reorder("name DESC").order("id DESC").collect { |dev| [dev.name, dev.id] } assert_equal expected, received end @@ -105,7 +105,7 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_unscope_after_reordering_and_combining expected = Developer.order(Arel.sql("id DESC, name DESC")).collect { |dev| [dev.name, dev.id] } - received = DeveloperOrderedBySalary.reorder(Arel.sql("name DESC")).unscope(:order).order(Arel.sql("id DESC, name DESC")).collect { |dev| [dev.name, dev.id] } + received = DeveloperOrderedBySalary.reorder("name DESC").unscope(:order).order(Arel.sql("id DESC, name DESC")).collect { |dev| [dev.name, dev.id] } assert_equal expected, received expected_2 = Developer.all.collect { |dev| [dev.name, dev.id] } @@ -113,60 +113,60 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected_2, received_2 expected_3 = Developer.all.collect { |dev| [dev.name, dev.id] } - received_3 = Developer.reorder(Arel.sql("name DESC")).unscope(:order).collect { |dev| [dev.name, dev.id] } + received_3 = Developer.reorder("name DESC").unscope(:order).collect { |dev| [dev.name, dev.id] } assert_equal expected_3, received_3 end def test_unscope_with_where_attributes - expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.where(name: "David").unscope(where: :name).collect(&:name) assert_equal expected, received - expected_2 = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected_2 = Developer.order("salary DESC").collect(&:name) received_2 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope({ where: :name }, :select).collect(&:name) assert_equal expected_2, received_2 - expected_3 = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected_3 = Developer.order("salary DESC").collect(&:name) received_3 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope(:select, :where).collect(&:name) assert_equal expected_3, received_3 - expected_4 = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected_4 = Developer.order("salary DESC").collect(&:name) received_4 = DeveloperOrderedBySalary.where.not("name" => "Jamis").unscope(where: :name).collect(&:name) assert_equal expected_4, received_4 - expected_5 = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected_5 = Developer.order("salary DESC").collect(&:name) received_5 = DeveloperOrderedBySalary.where.not("name" => ["Jamis", "David"]).unscope(where: :name).collect(&:name) assert_equal expected_5, received_5 - expected_6 = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected_6 = Developer.order("salary DESC").collect(&:name) received_6 = DeveloperOrderedBySalary.where(Developer.arel_table["name"].eq("David")).unscope(where: :name).collect(&:name) assert_equal expected_6, received_6 - expected_7 = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected_7 = Developer.order("salary DESC").collect(&:name) received_7 = DeveloperOrderedBySalary.where(Developer.arel_table[:name].eq("David")).unscope(where: :name).collect(&:name) assert_equal expected_7, received_7 end def test_unscope_comparison_where_clauses # unscoped for WHERE (`developers`.`id` <= 2) - expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.where(id: -Float::INFINITY..2).unscope(where: :id).collect { |dev| dev.name } assert_equal expected, received # unscoped for WHERE (`developers`.`id` < 2) - expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.where(id: -Float::INFINITY...2).unscope(where: :id).collect { |dev| dev.name } assert_equal expected, received end def test_unscope_multiple_where_clauses - expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.where(name: "Jamis").where(id: 1).unscope(where: [:name, :id]).collect(&:name) assert_equal expected, received end def test_unscope_string_where_clauses_involved - dev_relation = Developer.order(Arel.sql("salary DESC")).where("created_at > ?", 1.year.ago) + dev_relation = Developer.order("salary DESC").where("created_at > ?", 1.year.ago) expected = dev_relation.collect(&:name) dev_ordered_relation = DeveloperOrderedBySalary.where(name: "Jamis").where("created_at > ?", 1.year.ago) @@ -176,17 +176,17 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_unscope_with_grouping_attributes - expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect(&:name) assert_equal expected, received - expected_2 = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected_2 = Developer.order("salary DESC").collect(&:name) received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect(&:name) assert_equal expected_2, received_2 end def test_unscope_with_limit_in_query - expected = Developer.order(Arel.sql("salary DESC")).collect(&:name) + expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect(&:name) assert_equal expected, received end @@ -198,13 +198,13 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_unscope_reverse_order expected = Developer.all.collect(&:name) - received = Developer.order(Arel.sql("salary DESC")).reverse_order.unscope(:order).collect(&:name) + received = Developer.order("salary DESC").reverse_order.unscope(:order).collect(&:name) assert_equal expected, received end def test_unscope_select - expected = Developer.order(Arel.sql("salary ASC")).collect(&:name) - received = Developer.order(Arel.sql("salary DESC")).reverse_order.select(:name).unscope(:select).collect(&:name) + expected = Developer.order("salary ASC").collect(&:name) + received = Developer.order("salary DESC").reverse_order.select(:name).unscope(:select).collect(&:name) assert_equal expected, received expected_2 = Developer.all.collect(&:id) @@ -256,11 +256,11 @@ class DefaultScopingTest < ActiveRecord::TestCase end assert_raises(ArgumentError) do - Developer.order(Arel.sql("name DESC")).reverse_order.unscope(:reverse_order) + Developer.order("name DESC").reverse_order.unscope(:reverse_order) end assert_raises(ArgumentError) do - Developer.order(Arel.sql("name DESC")).where(name: "Jamis").unscope() + Developer.order("name DESC").where(name: "Jamis").unscope() end end @@ -295,7 +295,7 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_order_in_default_scope_should_not_prevail - expected = Developer.all.merge!(order: Arel.sql("salary desc")).to_a.collect(&:salary) + expected = Developer.all.merge!(order: "salary desc").to_a.collect(&:salary) received = DeveloperOrderedBySalary.all.merge!(order: "salary").to_a.collect(&:salary) assert_equal expected, received end diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index 4079bb477e..b0431a4e34 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -472,7 +472,7 @@ class NamedScopingTest < ActiveRecord::TestCase def test_scopes_on_relations # Topic.replied - approved_topics = Topic.all.approved.order(Arel.sql("id DESC")) + approved_topics = Topic.all.approved.order("id DESC") assert_equal topics(:fifth), approved_topics.first replied_approved_topics = approved_topics.replied @@ -480,7 +480,7 @@ class NamedScopingTest < ActiveRecord::TestCase end def test_index_on_scope - approved = Topic.approved.order(Arel.sql("id ASC")) + approved = Topic.approved.order("id ASC") assert_equal topics(:second), approved[0] assert approved.loaded? end diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index 2a95204d0d..116f8e83aa 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -39,23 +39,23 @@ class RelationScopingTest < ActiveRecord::TestCase end def test_reverse_order - assert_equal Developer.order(Arel.sql("id DESC")).to_a.reverse, Developer.order(Arel.sql("id DESC")).reverse_order + assert_equal Developer.order("id DESC").to_a.reverse, Developer.order("id DESC").reverse_order end def test_reverse_order_with_arel_node - assert_equal Developer.order(Arel.sql("id DESC")).to_a.reverse, Developer.order(Developer.arel_table[:id].desc).reverse_order + assert_equal Developer.order("id DESC").to_a.reverse, Developer.order(Developer.arel_table[:id].desc).reverse_order end def test_reverse_order_with_multiple_arel_nodes - assert_equal Developer.order(Arel.sql("id DESC")).order(Arel.sql("name DESC")).to_a.reverse, Developer.order(Developer.arel_table[:id].desc).order(Developer.arel_table[:name].desc).reverse_order + assert_equal Developer.order("id DESC").order("name DESC").to_a.reverse, Developer.order(Developer.arel_table[:id].desc).order(Developer.arel_table[:name].desc).reverse_order end def test_reverse_order_with_arel_nodes_and_strings - assert_equal Developer.order(Arel.sql("id DESC")).order(Arel.sql("name DESC")).to_a.reverse, Developer.order(Arel.sql("id DESC")).order(Developer.arel_table[:name].desc).reverse_order + assert_equal Developer.order("id DESC").order("name DESC").to_a.reverse, Developer.order("id DESC").order(Developer.arel_table[:name].desc).reverse_order end def test_double_reverse_order_produces_original_order - assert_equal Developer.order(Arel.sql("name DESC")), Developer.order(Arel.sql("name DESC")).reverse_order.reverse_order + assert_equal Developer.order("name DESC"), Developer.order("name DESC").reverse_order.reverse_order end def test_scoped_find @@ -72,7 +72,7 @@ class RelationScopingTest < ActiveRecord::TestCase end def test_scoped_find_last - highest_salary = Developer.order(Arel.sql("salary DESC")).first + highest_salary = Developer.order("salary DESC").first Developer.order("salary").scoping do assert_equal highest_salary, Developer.last @@ -80,8 +80,8 @@ class RelationScopingTest < ActiveRecord::TestCase end def test_scoped_find_last_preserves_scope - lowest_salary = Developer.order(Arel.sql("salary ASC")).first - highest_salary = Developer.order(Arel.sql("salary DESC")).first + lowest_salary = Developer.order("salary ASC").first + highest_salary = Developer.order("salary DESC").first Developer.order("salary").scoping do assert_equal highest_salary, Developer.last diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb index 74471b0ace..b32bb24ce8 100644 --- a/activerecord/test/cases/unsafe_raw_sql_test.rb +++ b/activerecord/test/cases/unsafe_raw_sql_test.rb @@ -77,10 +77,40 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_expected, ids_disabled end + test "order: allows table and column name" do + ids_expected = Post.order(Arel.sql("title")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order("posts.title").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("posts.title").pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + + test "order: allows column name and direction in string" do + ids_expected = Post.order(Arel.sql("title desc")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order("title desc").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("title desc").pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + + test "order: allows table name, column name and direction in string" do + ids_expected = Post.order(Arel.sql("title desc")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order("posts.title desc").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("posts.title desc").pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + test "order: disallows invalid column name" do with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do - Post.order("title asc").pluck(:id) + Post.order("foo asc").pluck(:id) end end end @@ -168,6 +198,16 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal values_expected, values_disabled end + test "pluck: allows table and column names" do + titles_expected = Post.pluck(Arel.sql("title")) + + titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("posts.title") } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("posts.title") } + + assert_equal titles_expected, titles_depr + assert_equal titles_expected, titles_disabled + end + test "pluck: disallows invalid column name" do with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 11fe073ab0..f462fdb69a 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -8,9 +8,9 @@ class Author < ActiveRecord::Base has_many :posts_with_comments, -> { includes(:comments) }, class_name: "Post" has_many :popular_grouped_posts, -> { includes(:comments).group("type").having("SUM(comments_count) > 1").select("type") }, class_name: "Post" has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order(Arel.sql("comments.id")) }, class_name: "Post" - has_many :posts_sorted_by_id_limited, -> { order(Arel.sql("posts.id")).limit(1) }, class_name: "Post" + has_many :posts_sorted_by_id_limited, -> { order("posts.id").limit(1) }, class_name: "Post" has_many :posts_with_categories, -> { includes(:categories) }, class_name: "Post" - has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order(Arel.sql("posts.id")) }, class_name: "Post" + has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, class_name: "Post" has_many :posts_with_special_categorizations, class_name: "PostWithSpecialCategorization" has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, class_name: "Post" has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, class_name: "Post" diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb index 0be943a321..3d6a7a96c2 100644 --- a/activerecord/test/models/car.rb +++ b/activerecord/test/models/car.rb @@ -19,13 +19,13 @@ class Car < ActiveRecord::Base scope :incl_tyres, -> { includes(:tyres) } scope :incl_engines, -> { includes(:engines) } - scope :order_using_new_style, -> { order(Arel.sql("name asc")) } + scope :order_using_new_style, -> { order("name asc") } end class CoolCar < Car - default_scope { order(Arel.sql("name desc")) } + default_scope { order("name desc") } end class FastCar < Car - default_scope { order(Arel.sql("name desc")) } + default_scope { order("name desc") } end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 1a82a9e646..bbc5fc2b2d 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -50,7 +50,7 @@ class Firm < Company has_many :clients, -> { order "id" }, dependent: :destroy, before_remove: :log_before_remove, after_remove: :log_after_remove has_many :unsorted_clients, class_name: "Client" has_many :unsorted_clients_with_symbol, class_name: :Client - has_many :clients_sorted_desc, -> { order Arel.sql("id DESC") }, class_name: "Client" + has_many :clients_sorted_desc, -> { order "id DESC" }, class_name: "Client" has_many :clients_of_firm, -> { order "id" }, foreign_key: "client_of", class_name: "Client", inverse_of: :firm has_many :clients_ordered_by_name, -> { order "name" }, class_name: "Client" has_many :unvalidated_clients_of_firm, foreign_key: "client_of", class_name: "Client", validate: false diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index 9108eb5249..52b7e06a63 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -9,7 +9,7 @@ module MyApplication class Firm < Company has_many :clients, -> { order("id") }, dependent: :destroy - has_many :clients_sorted_desc, -> { order(Arel.sql("id DESC")) }, class_name: "Client" + has_many :clients_sorted_desc, -> { order("id DESC") }, class_name: "Client" has_many :clients_of_firm, -> { order "id" }, foreign_key: "client_of", class_name: "Client" has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, class_name: "Client" has_one :account, class_name: "MyApplication::Billing::Account", dependent: :destroy diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index c2a21eac04..8881c69368 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -4,7 +4,7 @@ require "ostruct" module DeveloperProjectsAssociationExtension2 def find_least_recent - order(Arel.sql("id ASC")).first + order("id ASC").first end end @@ -13,7 +13,7 @@ class Developer < ActiveRecord::Base has_and_belongs_to_many :projects do def find_most_recent - order(Arel.sql("id DESC")).first + order("id DESC").first end end @@ -41,7 +41,7 @@ class Developer < ActiveRecord::Base join_table: "developers_projects", association_foreign_key: "project_id" do def find_least_recent - order(Arel.sql("id ASC")).first + order("id ASC").first end end @@ -126,7 +126,7 @@ end class DeveloperFilteredOnJoins < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" def self.default_scope joins(:projects).where(projects: { name: "Active Controller" }) @@ -135,9 +135,9 @@ end class DeveloperOrderedBySalary < ActiveRecord::Base self.table_name = "developers" - default_scope { order(Arel.sql("salary DESC")) } + default_scope { order("salary DESC") } - scope :by_name, -> { order(Arel.sql("name DESC")) } + scope :by_name, -> { order("name DESC") } end class DeveloperCalledDavid < ActiveRecord::Base @@ -225,14 +225,14 @@ end class EagerDeveloperWithDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" default_scope { includes(:projects) } end class EagerDeveloperWithClassMethodDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" def self.default_scope includes(:projects) @@ -241,21 +241,21 @@ end class EagerDeveloperWithLambdaDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" default_scope lambda { includes(:projects) } end class EagerDeveloperWithBlockDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" default_scope { includes(:projects) } end class EagerDeveloperWithCallableDefaultScope < ActiveRecord::Base self.table_name = "developers" - has_and_belongs_to_many :projects, -> { order(Arel.sql("projects.id")) }, foreign_key: "developer_id", join_table: "developers_projects" + has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects" default_scope OpenStruct.new(call: includes(:projects)) end diff --git a/activerecord/test/models/membership.rb b/activerecord/test/models/membership.rb index 47cb4d2146..09ee7544b3 100644 --- a/activerecord/test/models/membership.rb +++ b/activerecord/test/models/membership.rb @@ -12,7 +12,7 @@ class CurrentMembership < Membership end class SuperMembership < Membership - belongs_to :member, -> { order(Arel.sql("members.id DESC")) } + belongs_to :member, -> { order("members.id DESC") } belongs_to :club end diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index 99de37a52f..c8617d1cfe 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -3,7 +3,7 @@ class Pirate < ActiveRecord::Base belongs_to :parrot, validate: true belongs_to :non_validated_parrot, class_name: "Parrot" - has_and_belongs_to_many :parrots, -> { order(Arel.sql("parrots.id ASC")) }, validate: true + has_and_belongs_to_many :parrots, -> { order("parrots.id ASC") }, validate: true has_and_belongs_to_many :non_validated_parrots, class_name: "Parrot" has_and_belongs_to_many :parrots_with_method_callbacks, class_name: "Parrot", before_add: :log_before_add, @@ -23,7 +23,7 @@ class Pirate < ActiveRecord::Base has_one :ship has_one :update_only_ship, class_name: "Ship" has_one :non_validated_ship, class_name: "Ship" - has_many :birds, -> { order(Arel.sql("birds.id ASC")) } + has_many :birds, -> { order("birds.id ASC") } has_many :birds_with_method_callbacks, class_name: "Bird", before_add: :log_before_add, after_add: :log_after_add, diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 49bbbaaab7..4508f727d0 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -35,8 +35,8 @@ class Post < ActiveRecord::Base def first_comment super.body end - has_one :first_comment, -> { order(Arel.sql("id ASC")) }, class_name: "Comment" - has_one :last_comment, -> { order(Arel.sql("id desc")) }, class_name: "Comment" + has_one :first_comment, -> { order("id ASC") }, class_name: "Comment" + has_one :last_comment, -> { order("id desc") }, class_name: "Comment" scope :with_special_comments, -> { joins(:comments).where(comments: { type: "SpecialComment" }) } scope :with_very_special_comments, -> { joins(:comments).where(comments: { type: "VerySpecialComment" }) } @@ -52,7 +52,7 @@ class Post < ActiveRecord::Base has_many :comments do def find_most_recent - order(Arel.sql("id DESC")).first + order("id DESC").first end def newest @@ -323,5 +323,9 @@ class FakeKlass def enforce_raw_sql_whitelist(*args) # noop end + + def attribute_names_and_aliases + [] + end end end diff --git a/activerecord/test/models/tag.rb b/activerecord/test/models/tag.rb index e0d42f4f66..bc13c3a42d 100644 --- a/activerecord/test/models/tag.rb +++ b/activerecord/test/models/tag.rb @@ -11,6 +11,6 @@ end class OrderedTag < Tag self.table_name = "tags" - has_many :taggings, -> { order(Arel.sql("taggings.id DESC")) }, foreign_key: "tag_id" + has_many :taggings, -> { order("taggings.id DESC") }, foreign_key: "tag_id" has_many :tagged_posts, through: :taggings, source: "taggable", source_type: "Post" end -- cgit v1.2.3 From 798557145c727b2abef2487783f02e57f04197c9 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Wed, 11 Oct 2017 13:16:57 -0600 Subject: try using regexes --- .../lib/active_record/attribute_methods.rb | 48 +++++++-------------- .../lib/active_record/relation/calculations.rb | 12 ++---- .../lib/active_record/relation/query_methods.rb | 21 +--------- .../associations/cascaded_eager_loading_test.rb | 8 ++-- .../associations/eager_load_nested_include_test.rb | 2 +- activerecord/test/cases/associations/eager_test.rb | 20 ++++----- .../has_and_belongs_to_many_associations_test.rb | 2 +- .../associations/has_many_associations_test.rb | 2 +- .../has_one_through_associations_test.rb | 6 +-- .../associations/inverse_associations_test.rb | 8 ++-- .../test/cases/associations/join_model_test.rb | 2 +- .../nested_through_associations_test.rb | 6 +-- activerecord/test/cases/base_test.rb | 6 +-- activerecord/test/cases/calculations_test.rb | 14 +++---- activerecord/test/cases/finder_test.rb | 14 +++---- activerecord/test/cases/relation/merging_test.rb | 10 ++--- activerecord/test/cases/relation/mutation_test.rb | 2 +- activerecord/test/cases/relations_test.rb | 49 ++++++++++------------ .../test/cases/scoping/default_scoping_test.rb | 12 +++--- activerecord/test/cases/unsafe_raw_sql_test.rb | 4 +- activerecord/test/models/author.rb | 20 ++++----- activerecord/test/models/category.rb | 2 +- activerecord/test/models/comment.rb | 2 +- activerecord/test/models/owner.rb | 2 +- activerecord/test/models/person.rb | 2 +- activerecord/test/models/post.rb | 6 +-- activerecord/test/models/project.rb | 4 +- 27 files changed, 120 insertions(+), 166 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index ff381b4e0b..64f81ca582 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -167,12 +167,24 @@ module ActiveRecord end end - def enforce_raw_sql_whitelist(args, whitelist: attribute_names_and_aliases) # :nodoc: + # Regexp whitelist. Matches the following: + # "#{table_name}.#{column_name}" + # "#{column_name}" + COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i + + # Regexp whitelist. Matches the following: + # "#{table_name}.#{column_name}" + # "#{table_name}.#{column_name} #{direction}" + # "#{column_name}" + # "#{column_name} #{direction}" + COLUMN_NAME_ORDER_WHITELIST = /\A(?:\w+\.)?\w+(?:\s+asc|\s+desc)?\z/i + + def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc: unexpected = args.reject do |arg| - whitelist.include?(arg.to_s) || - arg.kind_of?(Arel::Node) || + arg.kind_of?(Arel::Node) || arg.is_a?(Arel::Nodes::SqlLiteral) || - arg.is_a?(Arel::Attributes::Attribute) + arg.is_a?(Arel::Attributes::Attribute) || + arg.to_s.split(/\s*,\s*/).all? { |part| whitelist.match?(part) } end return if unexpected.none? @@ -195,22 +207,6 @@ module ActiveRecord end end - # Can the given name be treated as a column name? Returns true if name - # is attribute or attribute alias. - # - # class Person < ActiveRecord::Base - # end - # - # Person.respond_to_attribute?(:name) - # # => true - # - # Person.respond_to_attribute?("foo") - # # => false - def respond_to_attribute?(name) # :nodoc: - name = name.to_s - attribute_names.include?(name) || attribute_aliases.include?(name) - end - # Returns true if the given attribute exists, otherwise false. # # class Person < ActiveRecord::Base @@ -242,18 +238,6 @@ module ActiveRecord ConnectionAdapters::NullColumn.new(name) end end - - # An Array of String attribute names and aliases for accessing those - # attributes. - # - # class Person < ActiveRecord::Base - # end - # - # Person.attribute_names_and_aliases - # # => ["id", "created_at", "updated_at", "name", "age"] - def attribute_names_and_aliases # :nodoc: - attribute_names + attribute_aliases.keys - end end # A Person object with a name attribute can ask person.respond_to?(:name), diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 75795fe493..d49472fc70 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -175,7 +175,7 @@ module ActiveRecord # See also #ids. # def pluck(*column_names) - if loaded? && (column_names.map(&:to_s) - @klass.attribute_names_and_aliases).empty? + if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty? return records.pluck(*column_names) end @@ -183,10 +183,10 @@ module ActiveRecord relation = apply_join_dependency relation.pluck(*column_names) else - enforce_raw_sql_whitelist(column_names, whitelist: allowed_pluck_columns) + enforce_raw_sql_whitelist(column_names) relation = spawn relation.select_values = column_names.map { |cn| - @klass.respond_to_attribute?(cn) ? arel_attribute(cn) : cn + @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn } result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) } result.cast_values(klass.attribute_types) @@ -203,12 +203,6 @@ module ActiveRecord private - def allowed_pluck_columns - @klass.attribute_names_and_aliases.map do |name| - [name, "#{table_name}.#{name}"] - end.flatten - end - def has_include?(column_name) eager_loading? || (includes_values.present? && column_name && column_name != :all) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 094e5aa733..59a732168c 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -299,7 +299,7 @@ module ActiveRecord def order!(*args) # :nodoc: @klass.enforce_raw_sql_whitelist( column_names_from_order_arguments(args), - whitelist: allowed_order_columns + whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST ) preprocess_order_args(args) @@ -326,7 +326,7 @@ module ActiveRecord def reorder!(*args) # :nodoc: @klass.enforce_raw_sql_whitelist( column_names_from_order_arguments(args), - whitelist: allowed_order_columns + whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST ) preprocess_order_args(args) @@ -928,20 +928,6 @@ module ActiveRecord private - def allowed_order_columns - @klass.attribute_names_and_aliases.map do |name| - [name, "#{table_name}.#{name}"].map do |name| - [ - name, - "#{name} asc", - "#{name} ASC", - "#{name} desc", - "#{name} DESC" - ] - end - end.flatten - end - # Extract column names from arguments passed to #order or #reorder. def column_names_from_order_arguments(args) args.flat_map { |arg| arg.is_a?(Hash) ? arg.keys : arg } @@ -1097,9 +1083,6 @@ module ActiveRecord when Arel::Nodes::Ordering o.reverse when String - # ensure we're not dealing with string subclass (Eg. Arel::Nodes::SqlLiteral) - o = String.new(o) - if does_not_support_reverse?(o) raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically" end diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index f08fd73da4..e69cfe5e52 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -121,7 +121,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase silly.parent_id = 1 assert silly.save - topics = Topic.all.merge!(includes: :replies, order: ["topics.id", Arel.sql("replies_topics.id")]).to_a + topics = Topic.all.merge!(includes: :replies, order: ["topics.id", "replies_topics.id"]).to_a assert_no_queries do assert_equal 2, topics[0].replies.size assert_equal 0, topics[1].replies.size @@ -129,14 +129,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_belongs_to_sti - replies = Reply.all.merge!(includes: :topic, order: Arel.sql("topics.id")).to_a + replies = Reply.all.merge!(includes: :topic, order: "topics.id").to_a assert_includes replies, topics(:second) assert_not_includes replies, topics(:first) assert_equal topics(:first), assert_no_queries { replies.first.topic } end def test_eager_association_loading_with_multiple_stis_and_order - author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: ["authors.name", Arel.sql("comments.body"), Arel.sql("very_special_comments_posts.body")], where: "posts.id = 4").first + author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first assert_equal authors(:david), author assert_no_queries do author.posts.first.special_comments @@ -145,7 +145,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_of_stis_with_multiple_references - authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: Arel.sql("comments.body, very_special_comments_posts.body"), where: "posts.id = 4").to_a + authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: "comments.body, very_special_comments_posts.body", where: "posts.id = 4").to_a assert_equal [authors(:david)], authors assert_no_queries do authors.first.posts.first.special_comments.first.post.special_comments 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 b1809401fb..c5b2b77bd4 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -120,7 +120,7 @@ class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase assert_nothing_raised do # @davey_mcdave doesn't have any author_favorites includes = { posts: :comments, categorizations: :category, author_favorites: :favorite_author } - Author.all.merge!(includes: includes, where: { authors: { name: @davey_mcdave.name } }, order: Arel.sql("categories.name")).to_a + Author.all.merge!(includes: includes, where: { authors: { name: @davey_mcdave.name } }, order: "categories.name").to_a end end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 75f851fec7..9a042c74db 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -62,7 +62,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_loading_with_one_association_with_non_preload - posts = Post.all.merge!(includes: :last_comment, order: Arel.sql("comments.id DESC")).to_a + posts = Post.all.merge!(includes: :last_comment, order: "comments.id DESC").to_a post = posts.find { |p| p.id == 1 } assert_equal Post.find(1).last_comment, post.last_comment end @@ -317,12 +317,12 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_nested_loading_through_has_one_association_with_order_on_association - aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: Arel.sql("authors.id")).find(author_addresses(:david_address).id) + aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "authors.id").find(author_addresses(:david_address).id) assert_equal aa.author.posts.count, aa.author.posts.length end def test_nested_loading_through_has_one_association_with_order_on_nested_association - aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: Arel.sql("posts.id")).find(author_addresses(:david_address).id) + aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "posts.id").find(author_addresses(:david_address).id) assert_equal aa.author.posts.count, aa.author.posts.length end @@ -420,7 +420,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_order_string_with_unquoted_table_name assert_nothing_raised do - Comment.all.merge!(includes: :post, order: Arel.sql("posts.id")).to_a + Comment.all.merge!(includes: :post, order: "posts.id").to_a end end @@ -1089,12 +1089,12 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_order_on_join_table_with_include_and_limit - assert_equal 5, Developer.all.merge!(includes: "projects", order: Arel.sql("developers_projects.joined_on DESC"), limit: 5).to_a.size + assert_equal 5, Developer.all.merge!(includes: "projects", order: "developers_projects.joined_on DESC", limit: 5).to_a.size end def test_eager_loading_with_order_on_joined_table_preloads posts = assert_queries(2) do - Post.all.merge!(joins: :comments, includes: :author, order: Arel.sql("comments.id DESC")).to_a + Post.all.merge!(joins: :comments, includes: :author, order: "comments.id DESC").to_a end assert_equal posts(:eager_other), posts[1] assert_equal authors(:mary), assert_no_queries { posts[1].author } @@ -1190,7 +1190,7 @@ class EagerAssociationTest < ActiveRecord::TestCase if current_adapter?(:OracleAdapter) firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies"[0, 30] + ".name").find(1) else - firm = Firm.all.merge!(includes: :clients_using_primary_key, order: Arel.sql("clients_using_primary_keys_companies.name")).find(1) + firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies.name").find(1) end assert_no_queries do assert_equal expected, firm.clients_using_primary_key @@ -1207,7 +1207,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_include_has_one_using_primary_key expected = accounts(:signals37) - firm = Firm.all.merge!(includes: :account_using_primary_key, order: Arel.sql("accounts.id")).to_a.detect { |f| f.id == 1 } + firm = Firm.all.merge!(includes: :account_using_primary_key, order: "accounts.id").to_a.detect { |f| f.id == 1 } assert_no_queries do assert_equal expected, firm.account_using_primary_key end @@ -1278,7 +1278,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_join_eager_with_empty_order_should_generate_valid_sql assert_nothing_raised do - Post.includes(:comments).order(Arel.sql("")).where(comments: { body: "Thank you for the welcome" }).first + Post.includes(:comments).order("").where(comments: { body: "Thank you for the welcome" }).first end end @@ -1382,7 +1382,7 @@ class EagerAssociationTest < ActiveRecord::TestCase test "preloading associations with string joins and order references" do author = assert_queries(2) { - Author.includes(:posts).joins("LEFT JOIN posts ON posts.author_id = authors.id").order(Arel.sql("posts.title DESC")).first + Author.includes(:posts).joins("LEFT JOIN posts ON posts.author_id = authors.id").order("posts.title DESC").first } assert_no_queries { assert_equal 5, author.posts.size 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 7e5655bb08..c817d7267b 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 @@ -587,7 +587,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase end def test_find_should_append_to_association_order - ordered_developers = projects(:active_record).developers.order(Arel.sql("projects.id")) + ordered_developers = projects(:active_record).developers.order("projects.id") assert_equal ["developers.name desc, developers.id desc", "projects.id"], ordered_developers.order_values end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 3597da7ff3..6bd11a5d81 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -550,7 +550,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_find_should_append_to_association_order - ordered_clients = companies(:first_firm).clients_sorted_desc.order(Arel.sql("companies.id")) + ordered_clients = companies(:first_firm).clients_sorted_desc.order("companies.id") assert_equal ["id DESC", "companies.id"], ordered_clients.order_values 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 bef20b2ebe..fe24c465b2 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -139,7 +139,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_nonpreload_eagerloading members = assert_queries(1) do - Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: Arel.sql("clubs.name")).to_a #force fallback + Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback end assert_equal 1, members.size assert_not_nil assert_no_queries { members[0].club } @@ -147,7 +147,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_nonpreload_eager_loading_through_polymorphic members = assert_queries(1) do - Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: Arel.sql("clubs.name")).to_a #force fallback + Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback end assert_equal 1, members.size assert_not_nil assert_no_queries { members[0].sponsor_club } @@ -156,7 +156,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record Sponsor.new(sponsor_club: clubs(:crazy_club), sponsorable: members(:groucho)).save! members = assert_queries(1) do - Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: Arel.sql("clubs.name DESC")).to_a #force fallback + Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name DESC").to_a #force fallback end assert_equal 1, members.size assert_not_nil assert_no_queries { members[0].sponsor_club } diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index c39e967569..e13cf93dcf 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -223,7 +223,7 @@ class InverseHasOneTests < ActiveRecord::TestCase f.man.name = "Mungo" assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance" - m = Man.all.merge!(where: { name: "Gordon" }, includes: :face, order: Arel.sql("faces.id")).first + m = Man.all.merge!(where: { name: "Gordon" }, includes: :face, order: "faces.id").first f = m.face assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance" m.name = "Bongo" @@ -329,7 +329,7 @@ class InverseHasManyTests < ActiveRecord::TestCase assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance" end - m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests, order: Arel.sql("interests.id")).first + m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests, order: "interests.id").first is = m.interests is.each do |i| assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance" @@ -554,7 +554,7 @@ class InverseBelongsToTests < ActiveRecord::TestCase m.face.description = "pleasing" assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance" - f = Face.all.merge!(includes: :man, order: Arel.sql("men.id"), where: { description: "trusting" }).first + f = Face.all.merge!(includes: :man, order: "men.id", where: { description: "trusting" }).first m = f.man assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance" f.description = "gormless" @@ -637,7 +637,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase m.polymorphic_face.description = "pleasing" assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance" - f = Face.all.merge!(where: { description: "confused" }, includes: :man, order: Arel.sql("men.id")).first + f = Face.all.merge!(where: { description: "confused" }, includes: :man, order: "men.id").first m = f.polymorphic_man assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance" f.description = "gormless" diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index a3acb1a152..87694b0788 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -420,7 +420,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_eager_load_has_many_through_has_many - author = Author.all.merge!(where: ["name = ?", "David"], includes: :comments, order: Arel.sql("comments.id")).first + author = Author.all.merge!(where: ["name = ?", "David"], includes: :comments, order: "comments.id").first SpecialComment.new; VerySpecialComment.new assert_no_queries do assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], author.comments.collect(&:id) diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 0254da9a99..65d30d011b 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -171,7 +171,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_one_with_has_many_through_source_reflection_preload_via_joins assert_includes_and_joins_equal( - Member.where("member_details.id" => member_details(:groucho).id).order(Arel.sql("member_details.id")), + Member.where("member_details.id" => member_details(:groucho).id).order("member_details.id"), [members(:groucho), members(:some_other_guy)], :organization_member_details ) @@ -203,7 +203,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_one_through_with_has_many_source_reflection_preload_via_joins assert_includes_and_joins_equal( - Member.where("member_details.id" => member_details(:groucho).id).order(Arel.sql("member_details.id")), + Member.where("member_details.id" => member_details(:groucho).id).order("member_details.id"), [members(:groucho), members(:some_other_guy)], :organization_member_details_2 ) @@ -341,7 +341,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload_via_joins assert_includes_and_joins_equal( - Categorization.where("taggings.id" => taggings(:welcome_general).id).order(Arel.sql("taggings.id")), + Categorization.where("taggings.id" => taggings(:welcome_general).id).order("taggings.id"), [categorizations(:david_welcome_general)], :post_taggings ) end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index a45b3a1644..f0ef522515 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1109,12 +1109,12 @@ class BasicsTest < ActiveRecord::TestCase end def test_find_multiple_ordered_last - last = Developer.all.merge!(order: Arel.sql("developers.name, developers.salary DESC")).last - assert_equal last, Developer.all.merge!(order: Arel.sql("developers.name, developers.salary DESC")).to_a.last + last = Developer.all.merge!(order: "developers.name, developers.salary DESC").last + assert_equal last, Developer.all.merge!(order: "developers.name, developers.salary DESC").to_a.last end def test_find_keeps_multiple_order_values - combined = Developer.all.merge!(order: Arel.sql("developers.name, developers.salary")).to_a + combined = Developer.all.merge!(order: "developers.name, developers.salary").to_a assert_equal combined, Developer.all.merge!(order: ["developers.name", "developers.salary"]).to_a end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 20dcb0080b..55b50e4f84 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -144,7 +144,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_should_order_by_calculation - c = Account.group(:firm_id).order(Arel.sql("sum_credit_limit desc, firm_id")).sum(:credit_limit) + c = Account.group(:firm_id).order("sum_credit_limit desc, firm_id").sum(:credit_limit) assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] } assert_equal [6, 2, 9, 1], c.keys.compact end @@ -644,7 +644,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_pluck_with_qualified_column_name - assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck(Arel.sql("topics.id")) + assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck("topics.id") end def test_pluck_auto_table_name_prefix @@ -659,7 +659,7 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_not_auto_table_name_prefix_if_column_joined Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)]) - assert_equal [7], Company.joins(:contracts).pluck(Arel.sql("developer_id")) + assert_equal [7], Company.joins(:contracts).pluck(:developer_id) end def test_pluck_with_selection_clause @@ -684,7 +684,7 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_not_auto_table_name_prefix_if_column_included Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)]) - ids = Company.includes(:contracts).pluck(Arel.sql("developer_id")) + ids = Company.includes(:contracts).pluck(:developer_id) assert_equal Company.count, ids.length assert_equal [7], ids.compact end @@ -704,12 +704,12 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_with_multiple_columns_and_selection_clause assert_equal [[1, 50], [2, 50], [3, 50], [4, 60], [5, 55], [6, 53]], - Account.pluck(Arel.sql("id, credit_limit")) + Account.pluck("id, credit_limit") end def test_pluck_with_multiple_columns_and_includes Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)]) - companies_and_developers = Company.order(Arel.sql("companies.id")).includes(:contracts).pluck(:name, Arel.sql("developer_id")) + companies_and_developers = Company.order("companies.id").includes(:contracts).pluck(:name, :developer_id) assert_equal Company.count, companies_and_developers.length assert_equal ["37signals", nil], companies_and_developers.first @@ -731,7 +731,7 @@ class CalculationsTest < ActiveRecord::TestCase def test_pluck_columns_with_same_name expected = [["The First Topic", "The Second Topic of the day"], ["The Third Topic of the day", "The Fourth Topic of the day"]] actual = Topic.joins(:replies) - .pluck(Arel.sql("topics.title"), Arel.sql("replies_topics.title")) + .pluck("topics.title", "replies_topics.title") assert_equal expected, actual end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 344d1a6639..1268949ba9 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -243,15 +243,15 @@ class FinderTest < ActiveRecord::TestCase end def test_exists_with_joins - assert_equal true, Topic.joins(:replies).where(replies_topics: { approved: true }).order(Arel.sql("replies_topics.created_at DESC")).exists? + assert_equal true, Topic.joins(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists? end def test_exists_with_left_joins - assert_equal true, Topic.left_joins(:replies).where(replies_topics: { approved: true }).order(Arel.sql("replies_topics.created_at DESC")).exists? + assert_equal true, Topic.left_joins(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists? end def test_exists_with_eager_load - assert_equal true, Topic.eager_load(:replies).where(replies_topics: { approved: true }).order(Arel.sql("replies_topics.created_at DESC")).exists? + assert_equal true, Topic.eager_load(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists? end def test_exists_with_includes_limit_and_empty_result @@ -267,8 +267,8 @@ class FinderTest < ActiveRecord::TestCase def test_exists_with_distinct_association_includes_limit_and_order author = Author.first - assert_equal false, author.unique_categorized_posts.includes(:special_comments).order(Arel.sql("comments.tags_count DESC")).limit(0).exists? - assert_equal true, author.unique_categorized_posts.includes(:special_comments).order(Arel.sql("comments.tags_count DESC")).limit(1).exists? + assert_equal false, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(0).exists? + assert_equal true, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(1).exists? end def test_exists_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association @@ -1125,11 +1125,11 @@ class FinderTest < ActiveRecord::TestCase def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct assert_equal 2, Post.includes(authors: :author_address). where.not(author_addresses: { id: nil }). - order(Arel.sql("author_addresses.id DESC")).limit(2).to_a.size + order("author_addresses.id DESC").limit(2).to_a.size assert_equal 3, Post.includes(author: :author_address, authors: :author_address). where.not(author_addresses_authors: { id: nil }). - order(Arel.sql("author_addresses_authors.id DESC")).limit(3).to_a.size + order("author_addresses_authors.id DESC").limit(3).to_a.size end def test_find_with_nil_inside_set_passed_for_one_attribute diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 8a656d7720..b68b3723f6 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -47,8 +47,8 @@ class RelationMergingTest < ActiveRecord::TestCase def test_relation_merging_with_eager_load relations = [] - relations << Post.order(Arel.sql("comments.id DESC")).merge(Post.eager_load(:last_comment)).merge(Post.all) - relations << Post.eager_load(:last_comment).merge(Post.order(Arel.sql("comments.id DESC"))).merge(Post.all) + relations << Post.order("comments.id DESC").merge(Post.eager_load(:last_comment)).merge(Post.all) + relations << Post.eager_load(:last_comment).merge(Post.order("comments.id DESC")).merge(Post.all) relations.each do |posts| post = posts.find { |p| p.id == 1 } @@ -126,19 +126,19 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase test "merging order relations" do posts_by_author_name = Post.limit(3).joins(:author). - merge(Author.order(:name)).pluck(Arel.sql("authors.name")) + merge(Author.order(:name)).pluck("authors.name") assert_equal ["Bob", "Bob", "David"], posts_by_author_name posts_by_author_name = Post.limit(3).joins(:author). - merge(Author.order(Arel.sql("name"))).pluck(Arel.sql("authors.name")) + merge(Author.order("name")).pluck("authors.name") assert_equal ["Bob", "Bob", "David"], posts_by_author_name end test "merging order relations (using a hash argument)" do posts_by_author_name = Post.limit(4).joins(:author). - merge(Author.order(name: :desc)).pluck(Arel.sql("authors.name")) + merge(Author.order(name: :desc)).pluck("authors.name") assert_equal ["Mary", "Mary", "Mary", "David"], posts_by_author_name end diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb index d845c07a50..ad3700b73a 100644 --- a/activerecord/test/cases/relation/mutation_test.rb +++ b/activerecord/test/cases/relation/mutation_test.rb @@ -94,7 +94,7 @@ module ActiveRecord end test "reverse_order!" do - @relation = Post.order(Arel.sql("title ASC, comments_count DESC")) + @relation = Post.order("title ASC, comments_count DESC") relation.reverse_order! diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ab2e432e78..6dfb78d913 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -220,7 +220,7 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_arel_assoc_order - topics = Topic.order("id" => :desc) + topics = Topic.order(Arel.sql("id") => :desc) assert_equal 5, topics.to_a.size assert_equal topics(:fifth).title, topics.first.title end @@ -232,13 +232,13 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_reversed_arel_assoc_order - topics = Topic.order("id" => :asc).reverse_order + topics = Topic.order(Arel.sql("id") => :asc).reverse_order assert_equal 5, topics.to_a.size assert_equal topics(:fifth).title, topics.first.title end def test_reverse_order_with_function - topics = Topic.order(Arel.sql("length(title)")).reverse_order + topics = Topic.order("length(title)").reverse_order assert_equal topics(:second).title, topics.first.title end @@ -248,9 +248,9 @@ class RelationTest < ActiveRecord::TestCase end def test_reverse_order_with_function_other_predicates - topics = Topic.order(Arel.sql("author_name, length(title), id")).reverse_order + topics = Topic.order("author_name, length(title), id").reverse_order assert_equal topics(:second).title, topics.first.title - topics = Topic.order(Arel.sql("length(author_name), id, length(title)")).reverse_order + topics = Topic.order("length(author_name), id, length(title)").reverse_order assert_equal topics(:fifth).title, topics.first.title end @@ -319,7 +319,7 @@ class RelationTest < ActiveRecord::TestCase end def test_raising_exception_on_invalid_hash_params - e = assert_raise(ArgumentError) { Topic.order(Arel.sql("name"), "id DESC", id: :asfsdf) } + e = assert_raise(ArgumentError) { Topic.order(:name, "id DESC", id: :asfsdf) } assert_equal 'Direction "asfsdf" is invalid. Valid directions are: [:asc, :desc, :ASC, :DESC, "asc", "desc", "ASC", "DESC"]', e.message end @@ -373,11 +373,8 @@ class RelationTest < ActiveRecord::TestCase def test_finding_with_cross_table_order_and_limit tags = Tag.includes(:taggings). - order( - "tags.name asc", - Arel.sql("taggings.taggable_id asc"), - Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)") - ).limit(1).to_a + order("tags.name asc", "taggings.taggable_id asc", Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)")). + limit(1).to_a assert_equal 1, tags.length end @@ -507,7 +504,7 @@ class RelationTest < ActiveRecord::TestCase def test_eager_association_loading_of_stis_with_multiple_references authors = Author.eager_load(posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } }). - order(Arel.sql("comments.body, very_special_comments_posts.body")).where("posts.id = 4").to_a + order("comments.body, very_special_comments_posts.body").where("posts.id = 4").to_a assert_equal [authors(:david)], authors assert_no_queries do @@ -580,7 +577,7 @@ class RelationTest < ActiveRecord::TestCase end def test_includes_with_select - query = Post.select("comments_count AS ranking").order(Arel.sql("ranking")).includes(:comments) + query = Post.select("comments_count AS ranking").order("ranking").includes(:comments) .where(comments: { id: 1 }) assert_equal ["comments_count AS ranking"], query.select_values @@ -649,9 +646,9 @@ class RelationTest < ActiveRecord::TestCase def test_to_sql_on_eager_join expected = assert_sql { - Post.eager_load(:last_comment).order(Arel.sql("comments.id DESC")).to_a + Post.eager_load(:last_comment).order("comments.id DESC").to_a }.first - actual = Post.eager_load(:last_comment).order(Arel.sql("comments.id DESC")).to_sql + actual = Post.eager_load(:last_comment).order("comments.id DESC").to_sql assert_equal expected, actual end @@ -662,7 +659,7 @@ class RelationTest < ActiveRecord::TestCase end def test_loading_with_one_association_with_non_preload - posts = Post.eager_load(:last_comment).order(Arel.sql("comments.id DESC")) + posts = Post.eager_load(:last_comment).order("comments.id DESC") post = posts.find { |p| p.id == 1 } assert_equal Post.find(1).last_comment, post.last_comment end @@ -1421,7 +1418,7 @@ class RelationTest < ActiveRecord::TestCase end def test_ordering_with_extra_spaces - assert_equal authors(:david), Author.order(Arel.sql("id DESC , name DESC")).last + assert_equal authors(:david), Author.order("id DESC , name DESC").last end def test_update_all_with_blank_argument @@ -1457,7 +1454,7 @@ class RelationTest < ActiveRecord::TestCase end def test_update_all_with_joins_and_offset_and_order - all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order(Arel.sql("posts.id"), "comments.id") + all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("posts.id", "comments.id") count = all_comments.count comments = all_comments.offset(1) @@ -1567,7 +1564,7 @@ class RelationTest < ActiveRecord::TestCase end def test_automatically_added_order_references - scope = Post.order(Arel.sql("comments.body")) + scope = Post.order("comments.body") assert_equal ["comments"], scope.references_values scope = Post.order(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")) @@ -1577,14 +1574,14 @@ class RelationTest < ActiveRecord::TestCase assert_equal ["comments"], scope.references_values end - scope = Post.order(Arel.sql("comments.body"), Arel.sql("yaks.body")) + scope = Post.order("comments.body", "yaks.body") assert_equal ["comments", "yaks"], scope.references_values # Don't infer yaks, let's not go down that road again... - scope = Post.order(Arel.sql("comments.body, yaks.body")) + scope = Post.order("comments.body, yaks.body") assert_equal ["comments"], scope.references_values - scope = Post.order(Arel.sql("comments.body asc")) + scope = Post.order("comments.body asc") assert_equal ["comments"], scope.references_values scope = Post.order(Arel.sql("foo(comments.body)")) @@ -1592,7 +1589,7 @@ class RelationTest < ActiveRecord::TestCase end def test_automatically_added_reorder_references - scope = Post.reorder(Arel.sql("comments.body")) + scope = Post.reorder("comments.body") assert_equal %w(comments), scope.references_values scope = Post.reorder(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")) @@ -1602,14 +1599,14 @@ class RelationTest < ActiveRecord::TestCase assert_equal ["comments"], scope.references_values end - scope = Post.reorder(Arel.sql("comments.body"), Arel.sql("yaks.body")) + scope = Post.reorder("comments.body", "yaks.body") assert_equal %w(comments yaks), scope.references_values # Don't infer yaks, let's not go down that road again... - scope = Post.reorder(Arel.sql("comments.body, yaks.body")) + scope = Post.reorder("comments.body, yaks.body") assert_equal %w(comments), scope.references_values - scope = Post.reorder(Arel.sql("comments.body asc")) + scope = Post.reorder("comments.body asc") assert_equal %w(comments), scope.references_values scope = Post.reorder(Arel.sql("foo(comments.body)")) diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 0d64db140c..716ca29eda 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -80,7 +80,7 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_scope_overwrites_default - expected = Developer.all.merge!(order: Arel.sql("salary DESC, name DESC")).to_a.collect(&:name) + expected = Developer.all.merge!(order: "salary DESC, name DESC").to_a.collect(&:name) received = DeveloperOrderedBySalary.by_name.to_a.collect(&:name) assert_equal expected, received end @@ -92,7 +92,7 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_order_after_reorder_combines_orders - expected = Developer.order(Arel.sql("name DESC, id DESC")).collect { |dev| [dev.name, dev.id] } + expected = Developer.order("name DESC, id DESC").collect { |dev| [dev.name, dev.id] } received = Developer.order("name ASC").reorder("name DESC").order("id DESC").collect { |dev| [dev.name, dev.id] } assert_equal expected, received end @@ -104,12 +104,12 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_unscope_after_reordering_and_combining - expected = Developer.order(Arel.sql("id DESC, name DESC")).collect { |dev| [dev.name, dev.id] } - received = DeveloperOrderedBySalary.reorder("name DESC").unscope(:order).order(Arel.sql("id DESC, name DESC")).collect { |dev| [dev.name, dev.id] } + expected = Developer.order("id DESC, name DESC").collect { |dev| [dev.name, dev.id] } + received = DeveloperOrderedBySalary.reorder("name DESC").unscope(:order).order("id DESC, name DESC").collect { |dev| [dev.name, dev.id] } assert_equal expected, received expected_2 = Developer.all.collect { |dev| [dev.name, dev.id] } - received_2 = Developer.order(Arel.sql("id DESC, name DESC")).unscope(:order).collect { |dev| [dev.name, dev.id] } + received_2 = Developer.order("id DESC, name DESC").unscope(:order).collect { |dev| [dev.name, dev.id] } assert_equal expected_2, received_2 expected_3 = Developer.all.collect { |dev| [dev.name, dev.id] } @@ -192,7 +192,7 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_order_to_unscope_reordering - scope = DeveloperOrderedBySalary.order(Arel.sql("salary DESC, name ASC")).reverse_order.unscope(:order) + scope = DeveloperOrderedBySalary.order("salary DESC, name ASC").reverse_order.unscope(:order) assert !/order/i.match?(scope.to_sql) end diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb index b32bb24ce8..18c6f4bae3 100644 --- a/activerecord/test/cases/unsafe_raw_sql_test.rb +++ b/activerecord/test/cases/unsafe_raw_sql_test.rb @@ -110,7 +110,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "order: disallows invalid column name" do with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do - Post.order("foo asc").pluck(:id) + Post.order("len(title) asc").pluck(:id) end end end @@ -126,7 +126,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "order: disallows invalid column with direction" do with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do - Post.order(foo: :asc).pluck(:id) + Post.order("len(title)" => :asc).pluck(:id) end end end diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index f462fdb69a..cb8686f315 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -7,7 +7,7 @@ class Author < ActiveRecord::Base has_many :very_special_comments, through: :posts has_many :posts_with_comments, -> { includes(:comments) }, class_name: "Post" has_many :popular_grouped_posts, -> { includes(:comments).group("type").having("SUM(comments_count) > 1").select("type") }, class_name: "Post" - has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order(Arel.sql("comments.id")) }, class_name: "Post" + has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order("comments.id") }, class_name: "Post" has_many :posts_sorted_by_id_limited, -> { order("posts.id").limit(1) }, class_name: "Post" has_many :posts_with_categories, -> { includes(:categories) }, class_name: "Post" has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, class_name: "Post" @@ -20,15 +20,15 @@ class Author < ActiveRecord::Base end end has_many :comments_containing_the_letter_e, through: :posts, source: :comments - has_many :comments_with_order_and_conditions, -> { order(Arel.sql("comments.body")).where("comments.body like 'Thank%'") }, through: :posts, source: :comments + has_many :comments_with_order_and_conditions, -> { order("comments.body").where("comments.body like 'Thank%'") }, through: :posts, source: :comments has_many :comments_with_include, -> { includes(:post).where(posts: { type: "Post" }) }, through: :posts, source: :comments has_many :comments_for_first_author, -> { for_first_author }, through: :posts, source: :comments has_many :first_posts - has_many :comments_on_first_posts, -> { order(Arel.sql("posts.id desc, comments.id asc")) }, through: :first_posts, source: :comments + has_many :comments_on_first_posts, -> { order("posts.id desc, comments.id asc") }, through: :first_posts, source: :comments has_one :first_post - has_one :comment_on_first_post, -> { order(Arel.sql("posts.id desc, comments.id asc")) }, through: :first_post, source: :comments + has_one :comment_on_first_post, -> { order("posts.id desc, comments.id asc") }, through: :first_post, source: :comments has_many :thinking_posts, -> { where(title: "So I was thinking") }, dependent: :delete_all, class_name: "Post" has_many :welcome_posts, -> { where(title: "Welcome to the weblog") }, class_name: "Post" @@ -40,11 +40,11 @@ class Author < ActiveRecord::Base -> { where(title: "Welcome to the weblog").where(Post.arel_table[:comments_count].gt(0)) }, class_name: "Post" - has_many :comments_desc, -> { order(Arel.sql("comments.id DESC")) }, through: :posts, source: :comments + has_many :comments_desc, -> { order("comments.id DESC") }, through: :posts, source: :comments has_many :unordered_comments, -> { unscope(:order).distinct }, through: :posts_sorted_by_id_limited, source: :comments has_many :funky_comments, through: :posts, source: :comments - has_many :ordered_uniq_comments, -> { distinct.order(Arel.sql("comments.id")) }, through: :posts, source: :comments - has_many :ordered_uniq_comments_desc, -> { distinct.order(Arel.sql("comments.id DESC")) }, through: :posts, source: :comments + has_many :ordered_uniq_comments, -> { distinct.order("comments.id") }, through: :posts, source: :comments + has_many :ordered_uniq_comments_desc, -> { distinct.order("comments.id DESC") }, through: :posts, source: :comments has_many :readonly_comments, -> { readonly }, through: :posts, source: :comments has_many :special_posts @@ -107,15 +107,15 @@ class Author < ActiveRecord::Base has_many :similar_posts, -> { distinct }, through: :tags, source: :tagged_posts has_many :ordered_posts, -> { distinct }, through: :ordered_tags, source: :tagged_posts - has_many :distinct_tags, -> { select("DISTINCT tags.*").order(Arel.sql("tags.name")) }, through: :posts, source: :tags + has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, through: :posts, source: :tags has_many :tags_with_primary_key, through: :posts has_many :books has_many :unpublished_books, -> { where(status: [:proposed, :written]) }, class_name: "Book" has_many :subscriptions, through: :books - has_many :subscribers, -> { order(Arel.sql("subscribers.nick")) }, through: :subscriptions - has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order(Arel.sql("subscribers.nick")) }, through: :subscriptions, source: :subscriber + has_many :subscribers, -> { order("subscribers.nick") }, through: :subscriptions + has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order("subscribers.nick") }, through: :subscriptions, source: :subscriber has_one :essay, primary_key: :name, as: :writer has_one :essay_category, through: :essay, source: :category diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb index 3038264694..2ccc00bed9 100644 --- a/activerecord/test/models/category.rb +++ b/activerecord/test/models/category.rb @@ -4,7 +4,7 @@ class Category < ActiveRecord::Base has_and_belongs_to_many :posts has_and_belongs_to_many :special_posts, class_name: "Post" has_and_belongs_to_many :other_posts, class_name: "Post" - has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, -> { includes(:authors).order(Arel.sql("authors.id")) }, class_name: "Post" + has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, -> { includes(:authors).order("authors.id") }, class_name: "Post" has_and_belongs_to_many :select_testing_posts, -> { select "posts.*, 1 as correctness_marker" }, diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index d5acfc0749..5ab433f2d9 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -81,7 +81,7 @@ class CommentThatAutomaticallyAltersPostBody < Comment end class CommentWithDefaultScopeReferencesAssociation < Comment - default_scope -> { includes(:developer).order(Arel.sql("developers.name")).references(:developer) } + default_scope -> { includes(:developer).order("developers.name").references(:developer) } belongs_to :developer end diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb index ebaafdec5e..5fa50d9918 100644 --- a/activerecord/test/models/owner.rb +++ b/activerecord/test/models/owner.rb @@ -2,7 +2,7 @@ class Owner < ActiveRecord::Base self.primary_key = :owner_id - has_many :pets, -> { order Arel.sql("pets.name desc") } + has_many :pets, -> { order "pets.name desc" } has_many :toys, through: :pets has_many :persons, through: :pets diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 7067f1b6b0..5cba1e440e 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -19,7 +19,7 @@ class Person < ActiveRecord::Base has_many :bad_references has_many :fixed_bad_references, -> { where favourite: true }, class_name: "BadReference" has_one :favourite_reference, -> { where "favourite=?", true }, class_name: "Reference" - has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order(Arel.sql("comments.id")) }, through: :readers, source: :post + has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order("comments.id") }, through: :readers, source: :post has_many :first_posts, -> { where(id: [1, 2]) }, through: :readers has_many :jobs, through: :references diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 4508f727d0..780a2c17f5 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -85,7 +85,7 @@ class Post < ActiveRecord::Base has_one :very_special_comment has_one :very_special_comment_with_post, -> { includes(:post) }, class_name: "VerySpecialComment" - has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order(Arel.sql("posts.id")) }, class_name: "VerySpecialComment" + has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order("posts.id") }, class_name: "VerySpecialComment" has_many :special_comments has_many :nonexistent_comments, -> { where "comments.id < 0" }, class_name: "Comment" @@ -323,9 +323,5 @@ class FakeKlass def enforce_raw_sql_whitelist(*args) # noop end - - def attribute_names_and_aliases - [] - end end end diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index 9b282a6729..846cef625b 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -2,9 +2,9 @@ class Project < ActiveRecord::Base belongs_to :mentor - has_and_belongs_to_many :developers, -> { distinct.order Arel.sql("developers.name desc, developers.id desc") } + has_and_belongs_to_many :developers, -> { distinct.order "developers.name desc, developers.id desc" } has_and_belongs_to_many :readonly_developers, -> { readonly }, class_name: "Developer" - has_and_belongs_to_many :non_unique_developers, -> { order Arel.sql("developers.name desc, developers.id desc") }, class_name: "Developer" + has_and_belongs_to_many :non_unique_developers, -> { order "developers.name desc, developers.id desc" }, class_name: "Developer" has_and_belongs_to_many :limited_developers, -> { limit 1 }, class_name: "Developer" has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, class_name: "Developer" has_and_belongs_to_many :developers_named_david_with_hash_conditions, -> { where(name: "David").distinct }, class_name: "Developer" -- cgit v1.2.3 From ab03eb9f576312c75e61caaf9705a8ac5175c769 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Wed, 11 Oct 2017 13:56:42 -0600 Subject: use << instead of #concat in #reverse_sql_order because we might be working with Arel SQL literator which overrides #concat --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- activerecord/test/cases/relations_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 59a732168c..34723b0b4f 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1088,7 +1088,7 @@ module ActiveRecord end o.split(",").map! do |s| s.strip! - s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || s.concat(" DESC") + s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC") end else o diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 6dfb78d913..f3f82e7591 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -238,7 +238,7 @@ class RelationTest < ActiveRecord::TestCase end def test_reverse_order_with_function - topics = Topic.order("length(title)").reverse_order + topics = Topic.order(Arel.sql("length(title)")).reverse_order assert_equal topics(:second).title, topics.first.title end -- cgit v1.2.3 From c711a27d29a3201ff47751a1d788f1e634186dd3 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Wed, 11 Oct 2017 14:15:47 -0600 Subject: convert order arg to string before checking if we can reverse it --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++++ activerecord/test/cases/relations_test.rb | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 34723b0b4f..db7fe8123d 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1097,6 +1097,10 @@ module ActiveRecord end def does_not_support_reverse?(order) + # Account for String subclasses like Arel::Nodes::SqlLiteral that + # override methods like #count. + order = String.new(order) unless order.instance_of?(String) + # Uses SQL function with multiple arguments. (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) || # Uses "nulls first" like construction. diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index f3f82e7591..4c865ef965 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -248,9 +248,9 @@ class RelationTest < ActiveRecord::TestCase end def test_reverse_order_with_function_other_predicates - topics = Topic.order("author_name, length(title), id").reverse_order + topics = Topic.order(Arel.sql("author_name, length(title), id")).reverse_order assert_equal topics(:second).title, topics.first.title - topics = Topic.order("length(author_name), id, length(title)").reverse_order + topics = Topic.order(Arel.sql("length(author_name), id, length(title)")).reverse_order assert_equal topics(:fifth).title, topics.first.title end -- cgit v1.2.3 From b76cc29865fb69389ffdb7bd9f8085aa86354f82 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Thu, 12 Oct 2017 11:48:48 -0600 Subject: deal with Array arguments to #order --- activerecord/lib/active_record/relation/query_methods.rb | 14 +++++++++++++- activerecord/lib/active_record/sanitization.rb | 6 ++++++ activerecord/test/cases/relations_test.rb | 6 +++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index db7fe8123d..1dcd786498 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -930,7 +930,19 @@ module ActiveRecord # Extract column names from arguments passed to #order or #reorder. def column_names_from_order_arguments(args) - args.flat_map { |arg| arg.is_a?(Hash) ? arg.keys : arg } + args.flat_map do |arg| + case arg + when Hash + # Tag.order(id: :desc) + arg.keys + when Array + # Tag.order([Arel.sql("field(id, ?)"), [1, 3, 2]]) + arg.flatten + else + # Tag.order(:id) + arg + end + end end def assert_mutability! diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 1c3099f55c..4caf1145f0 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -63,6 +63,12 @@ module ActiveRecord # # => "id ASC" def sanitize_sql_for_order(condition) # :doc: if condition.is_a?(Array) && condition.first.to_s.include?("?") + # Ensure we aren't dealing with a subclass of String that might + # override methods we use (eg. Arel::Nodes::SqlLiteral). + if condition.first.kind_of?(String) && !condition.first.instance_of?(String) + condition = [String.new(condition.first), *condition[1..-1]] + end + sanitize_sql_array(condition) else condition diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 4c865ef965..a755a3ceeb 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -389,13 +389,13 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_sanitized_order - query = Tag.order(["field(id, ?)", [1, 3, 2]]).to_sql + query = Tag.order([Arel.sql("field(id, ?)"), [1, 3, 2]]).to_sql assert_match(/field\(id, 1,3,2\)/, query) - query = Tag.order(["field(id, ?)", []]).to_sql + query = Tag.order([Arel.sql("field(id, ?)"), []]).to_sql assert_match(/field\(id, NULL\)/, query) - query = Tag.order(["field(id, ?)", nil]).to_sql + query = Tag.order([Arel.sql("field(id, ?)"), nil]).to_sql assert_match(/field\(id, NULL\)/, query) end -- cgit v1.2.3 From 8ef71ac4a119a4c03d78db2372b41ddcc8a95035 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Wed, 18 Oct 2017 10:21:45 -0600 Subject: push order arg checks down to allow for binds --- .../lib/active_record/relation/query_methods.rb | 33 ++++---------------- activerecord/lib/active_record/sanitization.rb | 6 +++- activerecord/test/cases/unsafe_raw_sql_test.rb | 36 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1dcd786498..cef0847651 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -297,11 +297,6 @@ module ActiveRecord # Same as #order but operates on relation in-place instead of copying. def order!(*args) # :nodoc: - @klass.enforce_raw_sql_whitelist( - column_names_from_order_arguments(args), - whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST - ) - preprocess_order_args(args) self.order_values += args @@ -324,11 +319,6 @@ module ActiveRecord # Same as #reorder but operates on relation in-place instead of copying. def reorder!(*args) # :nodoc: - @klass.enforce_raw_sql_whitelist( - column_names_from_order_arguments(args), - whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST - ) - preprocess_order_args(args) self.reordering_value = true @@ -928,23 +918,6 @@ module ActiveRecord private - # Extract column names from arguments passed to #order or #reorder. - def column_names_from_order_arguments(args) - args.flat_map do |arg| - case arg - when Hash - # Tag.order(id: :desc) - arg.keys - when Array - # Tag.order([Arel.sql("field(id, ?)"), [1, 3, 2]]) - arg.flatten - else - # Tag.order(:id) - arg - end - end - end - def assert_mutability! raise ImmutableRelation if @loaded raise ImmutableRelation if defined?(@arel) && @arel @@ -1146,6 +1119,12 @@ module ActiveRecord klass.send(:sanitize_sql_for_order, arg) end order_args.flatten! + + @klass.enforce_raw_sql_whitelist( + order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a }, + whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST + ) + validate_order_args(order_args) references = order_args.grep(String) diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 4caf1145f0..232743d3b6 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -63,13 +63,17 @@ module ActiveRecord # # => "id ASC" def sanitize_sql_for_order(condition) # :doc: if condition.is_a?(Array) && condition.first.to_s.include?("?") + enforce_raw_sql_whitelist([condition.first], + whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST + ) + # Ensure we aren't dealing with a subclass of String that might # override methods we use (eg. Arel::Nodes::SqlLiteral). if condition.first.kind_of?(String) && !condition.first.instance_of?(String) condition = [String.new(condition.first), *condition[1..-1]] end - sanitize_sql_array(condition) + Arel.sql(sanitize_sql_array(condition)) else condition end diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb index 18c6f4bae3..861df8f1da 100644 --- a/activerecord/test/cases/unsafe_raw_sql_test.rb +++ b/activerecord/test/cases/unsafe_raw_sql_test.rb @@ -138,6 +138,42 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_depr, ids_disabled end + test "order: allows Arel.sql with binds" do + ids_expected = Post.order(Arel.sql('INSTR(title, "comments"), id')).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order([Arel.sql("INSTR(title, ?), id"), "comments"]).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order([Arel.sql("INSTR(title, ?), id"), "comments"]).pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + + test "order: disallows invalid bind statement" do + with_unsafe_raw_sql_disabled do + assert_raises(ActiveRecord::UnknownAttributeReference) do + Post.order(["INSTR(title, ?), id", "comments"]).pluck(:id) + end + end + end + + test "order: disallows invalid Array arguments" do + with_unsafe_raw_sql_disabled do + assert_raises(ActiveRecord::UnknownAttributeReference) do + Post.order(["author_id", "length(title)"]).pluck(:id) + end + end + end + + test "order: allows valid Array arguments" do + ids_expected = Post.order(Arel.sql("author_id, length(title)")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("length(title)")]).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("length(title)")]).pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + test "order: logs deprecation warning for unrecognized column" do with_unsafe_raw_sql_deprecated do ActiveSupport::Deprecation.expects(:warn).with do |msg| -- cgit v1.2.3 From 4a5b3ca972e867d9b9276dcd98b0a6b9b6fb7583 Mon Sep 17 00:00:00 2001 From: Ben Toews Date: Thu, 26 Oct 2017 12:00:22 -0600 Subject: use database agnostic function/quoting in test --- activerecord/test/cases/unsafe_raw_sql_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb index 861df8f1da..5809d05327 100644 --- a/activerecord/test/cases/unsafe_raw_sql_test.rb +++ b/activerecord/test/cases/unsafe_raw_sql_test.rb @@ -139,10 +139,10 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end test "order: allows Arel.sql with binds" do - ids_expected = Post.order(Arel.sql('INSTR(title, "comments"), id')).pluck(:id) + ids_expected = Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz'), id")).pluck(:id) - ids_depr = with_unsafe_raw_sql_deprecated { Post.order([Arel.sql("INSTR(title, ?), id"), "comments"]).pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order([Arel.sql("INSTR(title, ?), id"), "comments"]).pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order([Arel.sql("REPLACE(title, ?, ?), id"), "misc", "zzzz"]).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order([Arel.sql("REPLACE(title, ?, ?), id"), "misc", "zzzz"]).pluck(:id) } assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled @@ -151,7 +151,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "order: disallows invalid bind statement" do with_unsafe_raw_sql_disabled do assert_raises(ActiveRecord::UnknownAttributeReference) do - Post.order(["INSTR(title, ?), id", "comments"]).pluck(:id) + Post.order(["REPLACE(title, ?, ?), id", "misc", "zzzz"]).pluck(:id) end end end -- cgit v1.2.3 From 2d5700b9a3e2ac25a5ff349ca6d3dc27077cea13 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 9 Nov 2017 22:09:00 +0900 Subject: Use `Dir.mktmpdir` As `@cache_path` is expected to be a directory name, use `Dir.mktmpdir`. And omit unnecessary `Dir.tmpdir`. --- actionpack/test/controller/log_subscriber_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 4c59bea193..cb425fb3de 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -98,7 +98,7 @@ class ACLogSubscriberTest < ActionController::TestCase @old_logger = ActionController::Base.logger - @cache_path = Tempfile.create(%w"tmp cache", Dir.tmpdir) + @cache_path = Dir.mktmpdir(%w"tmp cache") @controller.cache_store = :file_store, @cache_path ActionController::LogSubscriber.attach_to :action_controller end -- cgit v1.2.3 From 57f0e3d1300d01444d2a311560c055d26968dc3f Mon Sep 17 00:00:00 2001 From: Bogdan Gusiev Date: Mon, 6 Nov 2017 11:08:12 +0200 Subject: Remove code duplication in ActiveSupport::Cache --- activesupport/lib/active_support/cache.rb | 37 +++++++++---------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 976104e0c4..9f91f9b576 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -90,11 +90,16 @@ module ActiveSupport private def retrieve_cache_key(key) case - when key.respond_to?(:cache_key_with_version) then key.cache_key_with_version - when key.respond_to?(:cache_key) then key.cache_key - when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param - when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a) - else key.to_param + when key.respond_to?(:cache_key_with_version) + key.cache_key_with_version + when key.respond_to?(:cache_key) + key.cache_key + when key.is_a?(Hash) + key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }.to_param + when key.respond_to?(:to_a) + key.to_a.collect { |element| retrieve_cache_key(element) }.to_param + else + key.to_param end.to_s end @@ -565,33 +570,13 @@ module ActiveSupport # Prefixes a key with the namespace. Namespace and key will be delimited # with a colon. def normalize_key(key, options) - key = expanded_key(key) + key = Cache.expand_cache_key(key) namespace = options[:namespace] if options prefix = namespace.is_a?(Proc) ? namespace.call : namespace key = "#{prefix}:#{key}" if prefix key end - # 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) - return key.cache_key.to_s if key.respond_to?(:cache_key) - - case key - when Array - if key.size > 1 - key = key.collect { |element| expanded_key(element) } - else - key = key.first - end - when Hash - key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" } - end - - key.to_param - end - def normalize_version(key, options = nil) (options && options[:version].try(:to_param)) || expanded_version(key) end -- cgit v1.2.3 From 1f9f6f6cfc57020ccb35f77872c56f069f337075 Mon Sep 17 00:00:00 2001 From: Brent Wheeldon Date: Thu, 2 Nov 2017 14:53:43 -0400 Subject: Prevent deadlocks with load interlock and DB lock. This fixes an issue where competing threads deadlock each other. - Thread A holds the load interlock but is blocked on getting the DB lock - Thread B holds the DB lock but is blocked on getting the load interlock (for example when there is a `Model.transaction` block that needs to autoload) This solution allows for dependency loading in other threads while a thread is waiting to acquire the DB lock. Fixes #31019 --- .../connection_adapters/abstract_adapter.rb | 3 +- .../concurrency/load_interlock_aware_monitor.rb | 17 +++++++ .../load_interlock_aware_monitor_test.rb | 55 ++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 activesupport/lib/active_support/concurrency/load_interlock_aware_monitor.rb create mode 100644 activesupport/test/concurrency/load_interlock_aware_monitor_test.rb diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index e3aab8dad8..e91ef3b779 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -6,6 +6,7 @@ require "active_record/connection_adapters/schema_cache" require "active_record/connection_adapters/sql_type_metadata" require "active_record/connection_adapters/abstract/schema_dumper" require "active_record/connection_adapters/abstract/schema_creation" +require "active_support/concurrency/load_interlock_aware_monitor" require "arel/collectors/bind" require "arel/collectors/composite" require "arel/collectors/sql_string" @@ -108,7 +109,7 @@ module ActiveRecord @schema_cache = SchemaCache.new self @quoted_column_names, @quoted_table_names = {}, {} @visitor = arel_visitor - @lock = Monitor.new + @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true }) @prepared_statements = true diff --git a/activesupport/lib/active_support/concurrency/load_interlock_aware_monitor.rb b/activesupport/lib/active_support/concurrency/load_interlock_aware_monitor.rb new file mode 100644 index 0000000000..a8455c0048 --- /dev/null +++ b/activesupport/lib/active_support/concurrency/load_interlock_aware_monitor.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require "monitor" + +module ActiveSupport + module Concurrency + # A monitor that will permit dependency loading while blocked waiting for + # the lock. + class LoadInterlockAwareMonitor < Monitor + # Enters an exclusive section, but allows dependency loading while blocked + def mon_enter + mon_try_enter || + ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super } + end + end + end +end diff --git a/activesupport/test/concurrency/load_interlock_aware_monitor_test.rb b/activesupport/test/concurrency/load_interlock_aware_monitor_test.rb new file mode 100644 index 0000000000..2d0f45ec5f --- /dev/null +++ b/activesupport/test/concurrency/load_interlock_aware_monitor_test.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "concurrent/atomic/count_down_latch" +require "active_support/concurrency/load_interlock_aware_monitor" + +module ActiveSupport + module Concurrency + class LoadInterlockAwareMonitorTest < ActiveSupport::TestCase + def setup + @monitor = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new + end + + def test_entering_with_no_blocking + assert @monitor.mon_enter + end + + def test_entering_with_blocking + load_interlock_latch = Concurrent::CountDownLatch.new + monitor_latch = Concurrent::CountDownLatch.new + + able_to_use_monitor = false + able_to_load = false + + thread_with_load_interlock = Thread.new do + ActiveSupport::Dependencies.interlock.running do + load_interlock_latch.count_down + monitor_latch.wait + + @monitor.synchronize do + able_to_use_monitor = true + end + end + end + + thread_with_monitor_lock = Thread.new do + @monitor.synchronize do + monitor_latch.count_down + load_interlock_latch.wait + + ActiveSupport::Dependencies.interlock.loading do + able_to_load = true + end + end + end + + thread_with_load_interlock.join + thread_with_monitor_lock.join + + assert able_to_use_monitor + assert able_to_load + end + end + end +end -- cgit v1.2.3 From ead4776b82f838ee0630770d1852e8b02ac0f923 Mon Sep 17 00:00:00 2001 From: neumayr Date: Thu, 9 Nov 2017 17:37:06 +0100 Subject: Fix field_error_proc wrap form select optgroup and divider option tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Summary The [`:field_error_proc`](https://github.com/rails/rails/blob/master/actionview/lib/action_view/base.rb#L145) is responsible for decorating input tags that refer to attributes with errors. This default build-in rails feature wrap invalid form elements with additional markup: `
[…]
`. * Fix for `field_error_proc` wraps form select `optgroup` * Fix for `field_error_proc` wraps form select divider `option` * Add tests for uncovered elements with errors [Fixes #31088] #### Test coverage * `test_select_grouped_options_with_errors` * `test_time_zone_select_with_priority_zones_and_errors` #### Extend test coverage * `test_collection_select_with_errors` * `test_label_with_errors` * `test_check_box_with_errors` * `test_check_boxes_with_errors` * `test_radio_button_with_errors` * `test_radio_buttons_with_errors` * `test_collection_check_boxes_with_errors` * `test_collection_radio_buttons_with_errors` --- actionview/CHANGELOG.md | 6 ++ .../lib/action_view/helpers/active_model_helper.rb | 8 ++- .../test/template/active_model_helper_test.rb | 74 +++++++++++++++++++++- .../test/template/form_options_helper_test.rb | 19 ++++++ 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index e3227103d6..c700cb72ec 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,9 @@ +* Fix issues with `field_error_proc` wrapping `optgroup` and select divider `option`. + + Fixes #31088 + + *Matthias Neumayr* + * Remove deprecated Erubis ERB handler. *Rafael Mendonça França* diff --git a/actionview/lib/action_view/helpers/active_model_helper.rb b/actionview/lib/action_view/helpers/active_model_helper.rb index f1ef715710..e41a95d2ce 100644 --- a/actionview/lib/action_view/helpers/active_model_helper.rb +++ b/actionview/lib/action_view/helpers/active_model_helper.rb @@ -17,8 +17,8 @@ module ActionView end end - def content_tag(*) - error_wrapping(super) + def content_tag(type, options, *) + select_markup_helper?(type) ? super : error_wrapping(super) end def tag(type, options, *) @@ -43,6 +43,10 @@ module ActionView object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present? end + def select_markup_helper?(type) + ["optgroup", "option"].include?(type) + end + def tag_generate_errors?(options) options["type"] != "hidden" end diff --git a/actionview/test/template/active_model_helper_test.rb b/actionview/test/template/active_model_helper_test.rb index a929d8dc3d..36afed6dd6 100644 --- a/actionview/test/template/active_model_helper_test.rb +++ b/actionview/test/template/active_model_helper_test.rb @@ -6,7 +6,7 @@ class ActiveModelHelperTest < ActionView::TestCase tests ActionView::Helpers::ActiveModelHelper silence_warnings do - Post = Struct.new(:author_name, :body, :updated_at) do + Post = Struct.new(:author_name, :body, :category, :published, :updated_at) do include ActiveModel::Conversion include ActiveModel::Validations @@ -22,10 +22,14 @@ class ActiveModelHelperTest < ActionView::TestCase @post = Post.new @post.errors[:author_name] << "can't be empty" @post.errors[:body] << "foo" + @post.errors[:category] << "must exist" + @post.errors[:published] << "must be accepted" @post.errors[:updated_at] << "bar" @post.author_name = "" @post.body = "Back to the hill and over it again!" + @post.category = "rails" + @post.published = false @post.updated_at = Date.new(2004, 6, 15) end @@ -56,6 +60,25 @@ class ActiveModelHelperTest < ActionView::TestCase assert_dom_equal(expected_dom, select("post", "author_name", [:a, :b], prompt: "Choose one...")) end + def test_select_grouped_options_with_errors + grouped_options = [ + ["A", [["A1"], ["A2"]]], + ["B", [["B1"], ["B2"]]], + ] + + assert_dom_equal( + %(
), + select("post", "category", grouped_options) + ) + end + + def test_collection_select_with_errors + assert_dom_equal( + %(
), + collection_select("post", "author_name", [:a, :b], :to_s, :to_s) + ) + end + def test_date_select_with_errors assert_dom_equal( %(
\n\n\n
), @@ -77,6 +100,55 @@ class ActiveModelHelperTest < ActionView::TestCase ) end + def test_label_with_errors + assert_dom_equal( + %(
), + label("post", "body") + ) + end + + def test_check_box_with_errors + assert_dom_equal( + %(
), + check_box("post", "published") + ) + end + + def test_check_boxes_with_errors + assert_dom_equal( + %(
), + check_box("post", "published") + check_box("post", "published") + ) + end + + def test_radio_button_with_errors + assert_dom_equal( + %(
), + radio_button("post", "category", "rails") + ) + end + + def test_radio_buttons_with_errors + assert_dom_equal( + %(
), + radio_button("post", "category", "rails") + radio_button("post", "category", "java") + ) + end + + def test_collection_check_boxes_with_errors + assert_dom_equal( + %(
), + collection_check_boxes("post", "category", [:ruby, :java], :to_s, :to_s) + ) + end + + def test_collection_radio_buttons_with_errors + assert_dom_equal( + %(
), + collection_radio_buttons("post", "category", [:ruby, :java], :to_s, :to_s) + ) + end + def test_hidden_field_does_not_render_errors assert_dom_equal( %(), diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb index a66db2f3dc..f0eed1e290 100644 --- a/actionview/test/template/form_options_helper_test.rb +++ b/actionview/test/template/form_options_helper_test.rb @@ -1251,6 +1251,25 @@ class FormOptionsHelperTest < ActionView::TestCase html end + def test_time_zone_select_with_priority_zones_and_errors + @firm = Firm.new("D") + @firm.extend ActiveModel::Validations + @firm.errors[:time_zone] << "invalid" + zones = [ ActiveSupport::TimeZone.new("A"), ActiveSupport::TimeZone.new("D") ] + html = time_zone_select("firm", "time_zone", zones) + assert_dom_equal "
" \ + "" \ + "
", + html + end + def test_time_zone_select_with_default_time_zone_and_nil_value @firm = Firm.new() @firm.time_zone = nil -- cgit v1.2.3 From c3675f50d2e59b7fc173d7b332860c4b1a24a726 Mon Sep 17 00:00:00 2001 From: Lisa Ugray Date: Thu, 19 Oct 2017 12:45:07 -0400 Subject: Move Attribute and AttributeSet to ActiveModel Use these to back the attributes API. Stop automatically including ActiveModel::Dirty in ActiveModel::Attributes, and make it optional. --- activemodel/lib/active_model.rb | 2 + activemodel/lib/active_model/attribute.rb | 241 +++++++++++++++++++ .../attribute/user_provided_default.rb | 30 +++ .../lib/active_model/attribute_mutation_tracker.rb | 114 +++++++++ activemodel/lib/active_model/attribute_set.rb | 113 +++++++++ .../lib/active_model/attribute_set/builder.rb | 124 ++++++++++ .../lib/active_model/attribute_set/yaml_encoder.rb | 41 ++++ activemodel/lib/active_model/attributes.rb | 56 +++-- activemodel/lib/active_model/dirty.rb | 177 +++++++++----- activemodel/lib/active_model/type.rb | 4 + activemodel/test/cases/attribute_set_test.rb | 255 +++++++++++++++++++++ activemodel/test/cases/attribute_test.rb | 255 +++++++++++++++++++++ activemodel/test/cases/attributes_dirty_test.rb | 22 +- activemodel/test/cases/attributes_test.rb | 28 +-- activemodel/test/cases/dirty_test.rb | 4 + activerecord/lib/active_record.rb | 8 +- activerecord/lib/active_record/attribute.rb | 242 ------------------- .../attribute/user_provided_default.rb | 32 --- .../lib/active_record/attribute_methods/dirty.rb | 96 +------- .../active_record/attribute_mutation_tracker.rb | 111 --------- activerecord/lib/active_record/attribute_set.rb | 113 --------- .../lib/active_record/attribute_set/builder.rb | 126 ---------- .../active_record/attribute_set/yaml_encoder.rb | 43 ---- activerecord/lib/active_record/attributes.rb | 6 +- .../lib/active_record/legacy_yaml_adapter.rb | 2 +- activerecord/lib/active_record/model_schema.rb | 6 +- .../lib/active_record/relation/query_attribute.rb | 4 +- .../lib/active_record/relation/query_methods.rb | 4 +- activerecord/test/cases/attribute_set_test.rb | 255 --------------------- activerecord/test/cases/attribute_test.rb | 255 --------------------- 30 files changed, 1379 insertions(+), 1390 deletions(-) create mode 100644 activemodel/lib/active_model/attribute.rb create mode 100644 activemodel/lib/active_model/attribute/user_provided_default.rb create mode 100644 activemodel/lib/active_model/attribute_mutation_tracker.rb create mode 100644 activemodel/lib/active_model/attribute_set.rb create mode 100644 activemodel/lib/active_model/attribute_set/builder.rb create mode 100644 activemodel/lib/active_model/attribute_set/yaml_encoder.rb create mode 100644 activemodel/test/cases/attribute_set_test.rb create mode 100644 activemodel/test/cases/attribute_test.rb delete mode 100644 activerecord/lib/active_record/attribute.rb delete mode 100644 activerecord/lib/active_record/attribute/user_provided_default.rb delete mode 100644 activerecord/lib/active_record/attribute_mutation_tracker.rb delete mode 100644 activerecord/lib/active_record/attribute_set.rb delete mode 100644 activerecord/lib/active_record/attribute_set/builder.rb delete mode 100644 activerecord/lib/active_record/attribute_set/yaml_encoder.rb delete mode 100644 activerecord/test/cases/attribute_set_test.rb delete mode 100644 activerecord/test/cases/attribute_test.rb diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index a97d7989be..faecd6b96f 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -30,6 +30,8 @@ require "active_model/version" module ActiveModel extend ActiveSupport::Autoload + autoload :Attribute + autoload :Attributes autoload :AttributeAssignment autoload :AttributeMethods autoload :BlockValidator, "active_model/validator" diff --git a/activemodel/lib/active_model/attribute.rb b/activemodel/lib/active_model/attribute.rb new file mode 100644 index 0000000000..43130c37c5 --- /dev/null +++ b/activemodel/lib/active_model/attribute.rb @@ -0,0 +1,241 @@ +# frozen_string_literal: true + +module ActiveModel + class Attribute # :nodoc: + class << self + def from_database(name, value, type) + FromDatabase.new(name, value, type) + end + + def from_user(name, value, type, original_attribute = nil) + FromUser.new(name, value, type, original_attribute) + end + + def with_cast_value(name, value, type) + WithCastValue.new(name, value, type) + end + + def null(name) + Null.new(name) + end + + def uninitialized(name, type) + Uninitialized.new(name, type) + end + end + + attr_reader :name, :value_before_type_cast, :type + + # This method should not be called directly. + # Use #from_database or #from_user + def initialize(name, value_before_type_cast, type, original_attribute = nil) + @name = name + @value_before_type_cast = value_before_type_cast + @type = type + @original_attribute = original_attribute + end + + def value + # `defined?` is cheaper than `||=` when we get back falsy values + @value = type_cast(value_before_type_cast) unless defined?(@value) + @value + end + + def original_value + if assigned? + original_attribute.original_value + else + type_cast(value_before_type_cast) + end + end + + def value_for_database + type.serialize(value) + end + + def changed? + changed_from_assignment? || changed_in_place? + end + + def changed_in_place? + has_been_read? && type.changed_in_place?(original_value_for_database, value) + end + + def forgetting_assignment + with_value_from_database(value_for_database) + end + + def with_value_from_user(value) + type.assert_valid_value(value) + self.class.from_user(name, value, type, original_attribute || self) + end + + def with_value_from_database(value) + self.class.from_database(name, value, type) + end + + def with_cast_value(value) + self.class.with_cast_value(name, value, type) + end + + def with_type(type) + if changed_in_place? + with_value_from_user(value).with_type(type) + else + self.class.new(name, value_before_type_cast, type, original_attribute) + end + end + + def type_cast(*) + raise NotImplementedError + end + + def initialized? + true + end + + def came_from_user? + false + end + + def has_been_read? + defined?(@value) + end + + def ==(other) + self.class == other.class && + name == other.name && + value_before_type_cast == other.value_before_type_cast && + type == other.type + end + alias eql? == + + def hash + [self.class, name, value_before_type_cast, type].hash + end + + def init_with(coder) + @name = coder["name"] + @value_before_type_cast = coder["value_before_type_cast"] + @type = coder["type"] + @original_attribute = coder["original_attribute"] + @value = coder["value"] if coder.map.key?("value") + end + + def encode_with(coder) + coder["name"] = name + coder["value_before_type_cast"] = value_before_type_cast unless value_before_type_cast.nil? + coder["type"] = type if type + coder["original_attribute"] = original_attribute if original_attribute + coder["value"] = value if defined?(@value) + end + + 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 + end + end + + def changed_from_assignment? + assigned? && type.changed?(original_value, value, value_before_type_cast) + end + + def _original_value_for_database + type.serialize(original_value) + end + + class FromDatabase < Attribute # :nodoc: + def type_cast(value) + type.deserialize(value) + end + + def _original_value_for_database + value_before_type_cast + end + end + + class FromUser < Attribute # :nodoc: + def type_cast(value) + type.cast(value) + end + + def came_from_user? + !type.value_constructed_by_mass_assignment?(value_before_type_cast) + end + end + + class WithCastValue < Attribute # :nodoc: + def type_cast(value) + value + end + + def changed_in_place? + false + end + end + + class Null < Attribute # :nodoc: + def initialize(name) + super(name, nil, Type.default_value) + end + + def type_cast(*) + nil + end + + def with_type(type) + self.class.with_cast_value(name, nil, type) + end + + def with_value_from_database(value) + raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`" + end + alias_method :with_value_from_user, :with_value_from_database + end + + class Uninitialized < Attribute # :nodoc: + UNINITIALIZED_ORIGINAL_VALUE = Object.new + + def initialize(name, type) + super(name, nil, type) + end + + def value + if block_given? + yield name + end + end + + def original_value + UNINITIALIZED_ORIGINAL_VALUE + end + + def value_for_database + end + + def initialized? + false + end + + def with_type(type) + self.class.new(name, type) + end + end + + private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue + end +end diff --git a/activemodel/lib/active_model/attribute/user_provided_default.rb b/activemodel/lib/active_model/attribute/user_provided_default.rb new file mode 100644 index 0000000000..f274b687d4 --- /dev/null +++ b/activemodel/lib/active_model/attribute/user_provided_default.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "active_model/attribute" + +module ActiveModel + class Attribute # :nodoc: + class UserProvidedDefault < FromUser # :nodoc: + def initialize(name, value, type, database_default) + @user_provided_value = value + super(name, value, type, database_default) + end + + def value_before_type_cast + if user_provided_value.is_a?(Proc) + @memoized_value_before_type_cast ||= user_provided_value.call + else + @user_provided_value + end + end + + def with_type(type) + self.class.new(name, user_provided_value, type, original_attribute) + end + + protected + + attr_reader :user_provided_value + end + end +end diff --git a/activemodel/lib/active_model/attribute_mutation_tracker.rb b/activemodel/lib/active_model/attribute_mutation_tracker.rb new file mode 100644 index 0000000000..9072460124 --- /dev/null +++ b/activemodel/lib/active_model/attribute_mutation_tracker.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +module ActiveModel + class AttributeMutationTracker # :nodoc: + OPTION_NOT_GIVEN = Object.new + + def initialize(attributes) + @attributes = attributes + @forced_changes = Set.new + end + + def changed_values + attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result| + if changed?(attr_name) + result[attr_name] = attributes[attr_name].original_value + end + end + end + + def changes + attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result| + change = change_to_attribute(attr_name) + if change + result[attr_name] = change + end + end + end + + def change_to_attribute(attr_name) + attr_name = attr_name.to_s + if changed?(attr_name) + [attributes[attr_name].original_value, attributes.fetch_value(attr_name)] + end + end + + def any_changes? + attr_names.any? { |attr| changed?(attr) } + end + + def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) + attr_name = attr_name.to_s + forced_changes.include?(attr_name) || + attributes[attr_name].changed? && + (OPTION_NOT_GIVEN == from || attributes[attr_name].original_value == from) && + (OPTION_NOT_GIVEN == to || attributes[attr_name].value == to) + end + + def changed_in_place?(attr_name) + attributes[attr_name.to_s].changed_in_place? + end + + def forget_change(attr_name) + attr_name = attr_name.to_s + attributes[attr_name] = attributes[attr_name].forgetting_assignment + forced_changes.delete(attr_name) + end + + def original_value(attr_name) + attributes[attr_name.to_s].original_value + end + + def force_change(attr_name) + 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 + + private + + def attr_names + attributes.keys + end + end + + class NullMutationTracker # :nodoc: + include Singleton + + def changed_values(*) + {} + end + + def changes(*) + {} + end + + def change_to_attribute(attr_name) + end + + def any_changes?(*) + false + end + + def changed?(*) + false + end + + def changed_in_place?(*) + false + end + + def forget_change(*) + end + + def original_value(*) + end + + def force_change(*) + end + end +end diff --git a/activemodel/lib/active_model/attribute_set.rb b/activemodel/lib/active_model/attribute_set.rb new file mode 100644 index 0000000000..a892accbc6 --- /dev/null +++ b/activemodel/lib/active_model/attribute_set.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require "active_model/attribute_set/builder" +require "active_model/attribute_set/yaml_encoder" + +module ActiveModel + class AttributeSet # :nodoc: + delegate :each_value, :fetch, to: :attributes + + def initialize(attributes) + @attributes = attributes + end + + def [](name) + attributes[name] || Attribute.null(name) + end + + def []=(name, value) + attributes[name] = value + end + + def values_before_type_cast + attributes.transform_values(&:value_before_type_cast) + end + + def to_hash + initialized_attributes.transform_values(&:value) + end + alias_method :to_h, :to_hash + + def key?(name) + attributes.key?(name) && self[name].initialized? + end + + def keys + attributes.each_key.select { |name| self[name].initialized? } + end + + if defined?(JRUBY_VERSION) + # This form is significantly faster on JRuby, and this is one of our biggest hotspots. + # https://github.com/jruby/jruby/pull/2562 + def fetch_value(name, &block) + self[name].value(&block) + end + else + def fetch_value(name) + self[name].value { |n| yield n if block_given? } + end + end + + def write_from_database(name, value) + attributes[name] = self[name].with_value_from_database(value) + end + + def write_from_user(name, value) + attributes[name] = self[name].with_value_from_user(value) + end + + def write_cast_value(name, value) + attributes[name] = self[name].with_cast_value(value) + end + + def freeze + @attributes.freeze + super + end + + def deep_dup + self.class.allocate.tap do |copy| + copy.instance_variable_set(:@attributes, attributes.deep_dup) + end + end + + def initialize_dup(_) + @attributes = attributes.dup + super + end + + def initialize_clone(_) + @attributes = attributes.clone + super + end + + def reset(key) + if key?(key) + write_from_database(key, nil) + end + end + + def accessed + attributes.select { |_, attr| attr.has_been_read? }.keys + end + + def map(&block) + new_attributes = attributes.transform_values(&block) + AttributeSet.new(new_attributes) + end + + def ==(other) + attributes == other.attributes + end + + protected + + attr_reader :attributes + + private + + def initialized_attributes + attributes.select { |_, attr| attr.initialized? } + end + end +end diff --git a/activemodel/lib/active_model/attribute_set/builder.rb b/activemodel/lib/active_model/attribute_set/builder.rb new file mode 100644 index 0000000000..f94f47370f --- /dev/null +++ b/activemodel/lib/active_model/attribute_set/builder.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require "active_model/attribute" + +module ActiveModel + class AttributeSet # :nodoc: + class Builder # :nodoc: + attr_reader :types, :always_initialized, :default + + def initialize(types, always_initialized = nil, &default) + @types = types + @always_initialized = always_initialized + @default = default + end + + def build_from_database(values = {}, additional_types = {}) + if always_initialized && !values.key?(always_initialized) + values[always_initialized] = nil + end + + attributes = LazyAttributeHash.new(types, values, additional_types, &default) + AttributeSet.new(attributes) + end + end + end + + class LazyAttributeHash # :nodoc: + delegate :transform_values, :each_key, :each_value, :fetch, to: :materialize + + def initialize(types, values, additional_types, &default) + @types = types + @values = values + @additional_types = additional_types + @materialized = false + @delegate_hash = {} + @default = default || proc {} + end + + def key?(key) + delegate_hash.key?(key) || values.key?(key) || types.key?(key) + end + + def [](key) + delegate_hash[key] || assign_default_value(key) + end + + def []=(key, value) + if frozen? + raise RuntimeError, "Can't modify frozen hash" + end + delegate_hash[key] = value + end + + def deep_dup + dup.tap do |copy| + copy.instance_variable_set(:@delegate_hash, delegate_hash.transform_values(&:dup)) + end + end + + def initialize_dup(_) + @delegate_hash = Hash[delegate_hash] + super + end + + def select + keys = types.keys | values.keys | delegate_hash.keys + keys.each_with_object({}) do |key, hash| + attribute = self[key] + if yield(key, attribute) + hash[key] = attribute + end + end + end + + def ==(other) + if other.is_a?(LazyAttributeHash) + materialize == other.materialize + else + materialize == other + end + end + + def marshal_dump + materialize + end + + def marshal_load(delegate_hash) + @delegate_hash = delegate_hash + @types = {} + @values = {} + @additional_types = {} + @materialized = true + end + + protected + + attr_reader :types, :values, :additional_types, :delegate_hash, :default + + def materialize + unless @materialized + values.each_key { |key| self[key] } + types.each_key { |key| self[key] } + unless frozen? + @materialized = true + end + end + delegate_hash + end + + private + + def assign_default_value(name) + type = additional_types.fetch(name, types[name]) + value_present = true + value = values.fetch(name) { value_present = false } + + if value_present + delegate_hash[name] = Attribute.from_database(name, value, type) + elsif types.key?(name) + delegate_hash[name] = default.call(name) || Attribute.uninitialized(name, type) + end + end + end +end diff --git a/activemodel/lib/active_model/attribute_set/yaml_encoder.rb b/activemodel/lib/active_model/attribute_set/yaml_encoder.rb new file mode 100644 index 0000000000..4ea945b956 --- /dev/null +++ b/activemodel/lib/active_model/attribute_set/yaml_encoder.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module ActiveModel + class AttributeSet + # Attempts to do more intelligent YAML dumping of an + # ActiveModel::AttributeSet to reduce the size of the resulting string + class YAMLEncoder # :nodoc: + def initialize(default_types) + @default_types = default_types + end + + def encode(attribute_set, coder) + coder["concise_attributes"] = attribute_set.each_value.map do |attr| + if attr.type.equal?(default_types[attr.name]) + attr.with_type(nil) + else + attr + end + end + end + + def decode(coder) + if coder["attributes"] + coder["attributes"] + else + attributes_hash = Hash[coder["concise_attributes"].map do |attr| + if attr.type.nil? + attr = attr.with_type(default_types[attr.name]) + end + [attr.name, attr] + end] + AttributeSet.new(attributes_hash) + end + end + + protected + + attr_reader :default_types + end + end +end diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb index 3e34d3b83a..13cad87875 100644 --- a/activemodel/lib/active_model/attributes.rb +++ b/activemodel/lib/active_model/attributes.rb @@ -1,24 +1,30 @@ # frozen_string_literal: true +require "active_support/core_ext/object/deep_dup" require "active_model/type" +require "active_model/attribute_set" +require "active_model/attribute/user_provided_default" module ActiveModel module Attributes #:nodoc: extend ActiveSupport::Concern include ActiveModel::AttributeMethods - include ActiveModel::Dirty included do attribute_method_suffix "=" class_attribute :attribute_types, :_default_attributes, instance_accessor: false - self.attribute_types = {} - self._default_attributes = {} + self.attribute_types = Hash.new(Type.default_value) + self._default_attributes = AttributeSet.new({}) end module ClassMethods - def attribute(name, cast_type = Type::Value.new, **options) - self.attribute_types = attribute_types.merge(name.to_s => cast_type) - self._default_attributes = _default_attributes.merge(name.to_s => options[:default]) + def attribute(name, type = Type::Value.new, **options) + name = name.to_s + if type.is_a?(Symbol) + type = ActiveModel::Type.lookup(type, **options.except(:default)) + end + self.attribute_types = attribute_types.merge(name => type) + define_default_attribute(name, options.fetch(:default, NO_DEFAULT_PROVIDED), type) define_attribute_methods(name) end @@ -37,11 +43,29 @@ module ActiveModel undef_method :__temp__#{safe_name}= STR end + + NO_DEFAULT_PROVIDED = Object.new # :nodoc: + private_constant :NO_DEFAULT_PROVIDED + + def define_default_attribute(name, value, type) + self._default_attributes = _default_attributes.deep_dup + if value == NO_DEFAULT_PROVIDED + default_attribute = _default_attributes[name].with_type(type) + else + default_attribute = Attribute::UserProvidedDefault.new( + name, + value, + type, + _default_attributes.fetch(name.to_s) { nil }, + ) + end + _default_attributes[name] = default_attribute + end end def initialize(*) + @attributes = self.class._default_attributes.deep_dup super - clear_changes_information end private @@ -53,21 +77,17 @@ module ActiveModel attr_name.to_s end - cast_type = self.class.attribute_types[name] - - deserialized_value = ActiveModel::Type.lookup(cast_type).cast(value) - attribute_will_change!(name) unless deserialized_value == attribute(name) - instance_variable_set("@#{name}", deserialized_value) - deserialized_value + @attributes.write_from_user(attr_name.to_s, value) + value end - def attribute(name) - if instance_variable_defined?("@#{name}") - instance_variable_get("@#{name}") + def attribute(attr_name) + name = if self.class.attribute_alias?(attr_name) + self.class.attribute_alias(attr_name).to_s else - default = self.class._default_attributes[name] - default.respond_to?(:call) ? default.call : default + attr_name.to_s end + @attributes.fetch_value(name) end # Handle *= for method_missing. diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index 943db0ab52..ddd93e34a6 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -2,6 +2,7 @@ require "active_support/hash_with_indifferent_access" require "active_support/core_ext/object/duplicable" +require "active_model/attribute_mutation_tracker" module ActiveModel # == Active \Model \Dirty @@ -130,6 +131,24 @@ module ActiveModel attribute_method_affix prefix: "restore_", suffix: "!" end + def initialize_dup(other) # :nodoc: + super + if self.class.respond_to?(:_default_attributes) + @attributes = self.class._default_attributes.map do |attr| + attr.with_value_from_user(@attributes.fetch_value(attr.name)) + end + end + @mutations_from_database = nil + end + + def changes_applied # :nodoc: + @previously_changed = changes + @mutations_before_last_save = mutations_from_database + @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new + forget_attribute_assignments + @mutations_from_database = nil + end + # Returns +true+ if any of the attributes have unsaved changes, +false+ otherwise. # # person.changed? # => false @@ -148,6 +167,60 @@ module ActiveModel changed_attributes.keys end + # Handles *_changed? for +method_missing+. + def attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) # :nodoc: + !!changes_include?(attr) && + (to == OPTION_NOT_GIVEN || to == _read_attribute(attr)) && + (from == OPTION_NOT_GIVEN || from == changed_attributes[attr]) + end + + # Handles *_was for +method_missing+. + def attribute_was(attr) # :nodoc: + attribute_changed?(attr) ? changed_attributes[attr] : _read_attribute(attr) + end + + # Handles *_previously_changed? for +method_missing+. + def attribute_previously_changed?(attr) #:nodoc: + previous_changes_include?(attr) + end + + # Restore all previous data of the provided attributes. + def restore_attributes(attributes = changed) + attributes.each { |attr| restore_attribute! attr } + end + + # Clears all dirty data: current changes and previous changes. + def clear_changes_information + @previously_changed = ActiveSupport::HashWithIndifferentAccess.new + @mutations_before_last_save = nil + @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new + forget_attribute_assignments + @mutations_from_database = nil + end + + def clear_attribute_changes(attr_names) + attributes_changed_by_setter.except!(*attr_names) + attr_names.each do |attr_name| + clear_attribute_change(attr_name) + end + end + + # Returns a hash of the attributes with unsaved changes indicating their original + # values like attr => original value. + # + # person.name # => "bob" + # person.name = 'robert' + # person.changed_attributes # => {"name" => "bob"} + def changed_attributes + # This should only be set by methods which will call changed_attributes + # multiple times when it is known that the computed value cannot change. + if defined?(@cached_changed_attributes) + @cached_changed_attributes + else + attributes_changed_by_setter.reverse_merge(mutations_from_database.changed_values).freeze + end + end + # Returns a hash of changed attributes indicating their original # and new values like attr => [original value, new value]. # @@ -155,7 +228,9 @@ module ActiveModel # person.name = 'bob' # person.changes # => { "name" => ["bill", "bob"] } def changes - ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }] + cache_changed_attributes do + ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }] + end end # Returns a hash of attributes that were changed before the model was saved. @@ -166,45 +241,51 @@ module ActiveModel # person.previous_changes # => {"name" => ["bob", "robert"]} def previous_changes @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new + @previously_changed.merge(mutations_before_last_save.changes) end - # Returns a hash of the attributes with unsaved changes indicating their original - # values like attr => original value. - # - # person.name # => "bob" - # person.name = 'robert' - # person.changed_attributes # => {"name" => "bob"} - def changed_attributes - @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new + def attribute_changed_in_place?(attr_name) # :nodoc: + mutations_from_database.changed_in_place?(attr_name) end - # Handles *_changed? for +method_missing+. - def attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) # :nodoc: - !!changes_include?(attr) && - (to == OPTION_NOT_GIVEN || to == _read_attribute(attr)) && - (from == OPTION_NOT_GIVEN || from == changed_attributes[attr]) - end + private + def clear_attribute_change(attr_name) + mutations_from_database.forget_change(attr_name) + end - # Handles *_was for +method_missing+. - def attribute_was(attr) # :nodoc: - attribute_changed?(attr) ? changed_attributes[attr] : _read_attribute(attr) - end + def mutations_from_database + unless defined?(@mutations_from_database) + @mutations_from_database = nil + end + @mutations_from_database ||= if @attributes + ActiveModel::AttributeMutationTracker.new(@attributes) + else + NullMutationTracker.instance + end + end - # Handles *_previously_changed? for +method_missing+. - def attribute_previously_changed?(attr) #:nodoc: - previous_changes_include?(attr) - end + def forget_attribute_assignments + @attributes = @attributes.map(&:forgetting_assignment) if @attributes + end - # Restore all previous data of the provided attributes. - def restore_attributes(attributes = changed) - attributes.each { |attr| restore_attribute! attr } - end + def mutations_before_last_save + @mutations_before_last_save ||= ActiveModel::NullMutationTracker.instance + end - private + def cache_changed_attributes + @cached_changed_attributes = changed_attributes + yield + ensure + clear_changed_attributes_cache + end + + def clear_changed_attributes_cache + remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes) + end # Returns +true+ if attr_name is changed, +false+ otherwise. def changes_include?(attr_name) - attributes_changed_by_setter.include?(attr_name) + attributes_changed_by_setter.include?(attr_name) || mutations_from_database.changed?(attr_name) end alias attribute_changed_by_setter? changes_include? @@ -214,18 +295,6 @@ module ActiveModel previous_changes.include?(attr_name) end - # Removes current changes and makes them accessible through +previous_changes+. - def changes_applied # :doc: - @previously_changed = changes - @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new - end - - # Clears all dirty data: current changes and previous changes. - def clear_changes_information # :doc: - @previously_changed = ActiveSupport::HashWithIndifferentAccess.new - @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new - end - # Handles *_change for +method_missing+. def attribute_change(attr) [changed_attributes[attr], _read_attribute(attr)] if attribute_changed?(attr) @@ -238,15 +307,16 @@ module ActiveModel # Handles *_will_change! for +method_missing+. def attribute_will_change!(attr) - return if attribute_changed?(attr) + unless attribute_changed?(attr) + begin + value = _read_attribute(attr) + value = value.duplicable? ? value.clone : value + rescue TypeError, NoMethodError + end - begin - value = _read_attribute(attr) - value = value.duplicable? ? value.clone : value - rescue TypeError, NoMethodError + set_attribute_was(attr, value) end - - set_attribute_was(attr, value) + mutations_from_database.force_change(attr) end # Handles restore_*! for +method_missing+. @@ -257,18 +327,13 @@ module ActiveModel end end - # This is necessary because `changed_attributes` might be overridden in - # other implementations (e.g. in `ActiveRecord`) - alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc: + def attributes_changed_by_setter + @attributes_changed_by_setter ||= ActiveSupport::HashWithIndifferentAccess.new + end # Force an attribute to have a particular "before" value def set_attribute_was(attr, old_value) attributes_changed_by_setter[attr] = old_value end - - # Remove changes information for the provided attributes. - def clear_attribute_changes(attributes) # :doc: - attributes_changed_by_setter.except!(*attributes) - end end end diff --git a/activemodel/lib/active_model/type.rb b/activemodel/lib/active_model/type.rb index b0ed67f28e..39324999c9 100644 --- a/activemodel/lib/active_model/type.rb +++ b/activemodel/lib/active_model/type.rb @@ -32,6 +32,10 @@ module ActiveModel def lookup(*args, **kwargs) # :nodoc: registry.lookup(*args, **kwargs) end + + def default_value # :nodoc: + @default_value ||= Value.new + end end register(:big_integer, Type::BigInteger) diff --git a/activemodel/test/cases/attribute_set_test.rb b/activemodel/test/cases/attribute_set_test.rb new file mode 100644 index 0000000000..d50e6cfa7a --- /dev/null +++ b/activemodel/test/cases/attribute_set_test.rb @@ -0,0 +1,255 @@ +# frozen_string_literal: true + +require "cases/helper" + +module ActiveModel + class AttributeSetTest < ActiveModel::TestCase + test "building a new set from raw attributes" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) + attributes = builder.build_from_database(foo: "1.1", bar: "2.2") + + assert_equal 1, attributes[:foo].value + assert_equal 2.2, attributes[:bar].value + assert_equal :foo, attributes[:foo].name + assert_equal :bar, attributes[:bar].name + end + + test "building with custom types" do + builder = AttributeSet::Builder.new(foo: Type::Float.new) + attributes = builder.build_from_database({ foo: "3.3", bar: "4.4" }, { bar: Type::Integer.new }) + + assert_equal 3.3, attributes[:foo].value + assert_equal 4, attributes[:bar].value + end + + test "[] returns a null object" do + builder = AttributeSet::Builder.new(foo: Type::Float.new) + attributes = builder.build_from_database(foo: "3.3") + + assert_equal "3.3", attributes[:foo].value_before_type_cast + assert_nil attributes[:bar].value_before_type_cast + assert_equal :bar, attributes[:bar].name + end + + test "duping creates a new hash, but does not dup the attributes" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new) + attributes = builder.build_from_database(foo: 1, bar: "foo") + + # Ensure the type cast value is cached + attributes[:foo].value + attributes[:bar].value + + duped = attributes.dup + duped.write_from_database(:foo, 2) + duped[:bar].value << "bar" + + assert_equal 1, attributes[:foo].value + assert_equal 2, duped[:foo].value + assert_equal "foobar", attributes[:bar].value + assert_equal "foobar", duped[:bar].value + end + + test "deep_duping creates a new hash and dups each attribute" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new) + attributes = builder.build_from_database(foo: 1, bar: "foo") + + # Ensure the type cast value is cached + attributes[:foo].value + attributes[:bar].value + + duped = attributes.deep_dup + duped.write_from_database(:foo, 2) + duped[:bar].value << "bar" + + assert_equal 1, attributes[:foo].value + assert_equal 2, duped[:foo].value + assert_equal "foo", attributes[:bar].value + assert_equal "foobar", duped[:bar].value + end + + test "freezing cloned set does not freeze original" do + attributes = AttributeSet.new({}) + clone = attributes.clone + + clone.freeze + + assert clone.frozen? + assert_not attributes.frozen? + end + + test "to_hash returns a hash of the type cast values" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) + attributes = builder.build_from_database(foo: "1.1", bar: "2.2") + + assert_equal({ foo: 1, bar: 2.2 }, attributes.to_hash) + assert_equal({ foo: 1, bar: 2.2 }, attributes.to_h) + end + + test "to_hash maintains order" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) + attributes = builder.build_from_database(foo: "2.2", bar: "3.3") + + attributes[:bar] + hash = attributes.to_h + + assert_equal [[:foo, 2], [:bar, 3.3]], hash.to_a + end + + test "values_before_type_cast" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new) + attributes = builder.build_from_database(foo: "1.1", bar: "2.2") + + assert_equal({ foo: "1.1", bar: "2.2" }, attributes.values_before_type_cast) + end + + test "known columns are built with uninitialized attributes" do + attributes = attributes_with_uninitialized_key + assert attributes[:foo].initialized? + assert_not attributes[:bar].initialized? + end + + test "uninitialized attributes are not included in the attributes hash" do + attributes = attributes_with_uninitialized_key + assert_equal({ foo: 1 }, attributes.to_hash) + end + + test "uninitialized attributes are not included in keys" do + attributes = attributes_with_uninitialized_key + assert_equal [:foo], attributes.keys + end + + test "uninitialized attributes return false for key?" do + attributes = attributes_with_uninitialized_key + assert attributes.key?(:foo) + assert_not attributes.key?(:bar) + end + + test "unknown attributes return false for key?" do + attributes = attributes_with_uninitialized_key + assert_not attributes.key?(:wibble) + end + + test "fetch_value returns the value for the given initialized attribute" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) + attributes = builder.build_from_database(foo: "1.1", bar: "2.2") + + assert_equal 1, attributes.fetch_value(:foo) + assert_equal 2.2, attributes.fetch_value(:bar) + end + + test "fetch_value returns nil for unknown attributes" do + attributes = attributes_with_uninitialized_key + assert_nil attributes.fetch_value(:wibble) { "hello" } + end + + test "fetch_value returns nil for unknown attributes when types has a default" do + types = Hash.new(Type::Value.new) + builder = AttributeSet::Builder.new(types) + attributes = builder.build_from_database + + assert_nil attributes.fetch_value(:wibble) { "hello" } + end + + test "fetch_value uses the given block for uninitialized attributes" do + attributes = attributes_with_uninitialized_key + value = attributes.fetch_value(:bar) { |n| n.to_s + "!" } + assert_equal "bar!", value + end + + test "fetch_value returns nil for uninitialized attributes if no block is given" do + attributes = attributes_with_uninitialized_key + assert_nil attributes.fetch_value(:bar) + end + + test "the primary_key is always initialized" do + builder = AttributeSet::Builder.new({ foo: Type::Integer.new }, :foo) + attributes = builder.build_from_database + + assert attributes.key?(:foo) + assert_equal [:foo], attributes.keys + assert attributes[:foo].initialized? + end + + class MyType + def cast(value) + return if value.nil? + value + " from user" + end + + def deserialize(value) + return if value.nil? + value + " from database" + end + + def assert_valid_value(*) + end + end + + test "write_from_database sets the attribute with database typecasting" do + builder = AttributeSet::Builder.new(foo: MyType.new) + attributes = builder.build_from_database + + assert_nil attributes.fetch_value(:foo) + + attributes.write_from_database(:foo, "value") + + assert_equal "value from database", attributes.fetch_value(:foo) + end + + test "write_from_user sets the attribute with user typecasting" do + builder = AttributeSet::Builder.new(foo: MyType.new) + attributes = builder.build_from_database + + assert_nil attributes.fetch_value(:foo) + + attributes.write_from_user(:foo, "value") + + assert_equal "value from user", attributes.fetch_value(:foo) + end + + def attributes_with_uninitialized_key + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) + builder.build_from_database(foo: "1.1") + end + + test "freezing doesn't prevent the set from materializing" do + builder = AttributeSet::Builder.new(foo: Type::String.new) + attributes = builder.build_from_database(foo: "1") + + attributes.freeze + assert_equal({ foo: "1" }, attributes.to_hash) + end + + test "#accessed_attributes returns only attributes which have been read" do + builder = AttributeSet::Builder.new(foo: Type::Value.new, bar: Type::Value.new) + attributes = builder.build_from_database(foo: "1", bar: "2") + + assert_equal [], attributes.accessed + + attributes.fetch_value(:foo) + + assert_equal [:foo], attributes.accessed + end + + test "#map returns a new attribute set with the changes applied" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new) + attributes = builder.build_from_database(foo: "1", bar: "2") + new_attributes = attributes.map do |attr| + attr.with_cast_value(attr.value + 1) + end + + assert_equal 2, new_attributes.fetch_value(:foo) + assert_equal 3, new_attributes.fetch_value(:bar) + end + + test "comparison for equality is correctly implemented" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new) + attributes = builder.build_from_database(foo: "1", bar: "2") + attributes2 = builder.build_from_database(foo: "1", bar: "2") + attributes3 = builder.build_from_database(foo: "2", bar: "2") + + assert_equal attributes, attributes2 + assert_not_equal attributes2, attributes3 + end + end +end diff --git a/activemodel/test/cases/attribute_test.rb b/activemodel/test/cases/attribute_test.rb new file mode 100644 index 0000000000..14d86cef97 --- /dev/null +++ b/activemodel/test/cases/attribute_test.rb @@ -0,0 +1,255 @@ +# frozen_string_literal: true + +require "cases/helper" + +module ActiveModel + class AttributeTest < ActiveModel::TestCase + setup do + @type = Minitest::Mock.new + end + + teardown do + assert @type.verify + end + + test "from_database + read type casts from database" do + @type.expect(:deserialize, "type cast from database", ["a value"]) + attribute = Attribute.from_database(nil, "a value", @type) + + type_cast_value = attribute.value + + assert_equal "type cast from database", type_cast_value + end + + test "from_user + read type casts from user" do + @type.expect(:cast, "type cast from user", ["a value"]) + attribute = Attribute.from_user(nil, "a value", @type) + + type_cast_value = attribute.value + + assert_equal "type cast from user", type_cast_value + end + + test "reading memoizes the value" do + @type.expect(:deserialize, "from the database", ["whatever"]) + attribute = Attribute.from_database(nil, "whatever", @type) + + type_cast_value = attribute.value + second_read = attribute.value + + assert_equal "from the database", type_cast_value + assert_same type_cast_value, second_read + end + + test "reading memoizes falsy values" do + @type.expect(:deserialize, false, ["whatever"]) + attribute = Attribute.from_database(nil, "whatever", @type) + + attribute.value + attribute.value + end + + test "read_before_typecast returns the given value" do + attribute = Attribute.from_database(nil, "raw value", @type) + + raw_value = attribute.value_before_type_cast + + assert_equal "raw value", raw_value + end + + test "from_database + read_for_database type casts to and from database" do + @type.expect(:deserialize, "read from database", ["whatever"]) + @type.expect(:serialize, "ready for database", ["read from database"]) + attribute = Attribute.from_database(nil, "whatever", @type) + + serialize = attribute.value_for_database + + assert_equal "ready for database", serialize + end + + test "from_user + read_for_database type casts from the user to the database" do + @type.expect(:cast, "read from user", ["whatever"]) + @type.expect(:serialize, "ready for database", ["read from user"]) + attribute = Attribute.from_user(nil, "whatever", @type) + + serialize = attribute.value_for_database + + assert_equal "ready for database", serialize + end + + test "duping dups the value" do + @type.expect(:deserialize, "type cast".dup, ["a value"]) + attribute = Attribute.from_database(nil, "a value", @type) + + value_from_orig = attribute.value + value_from_clone = attribute.dup.value + value_from_orig << " foo" + + assert_equal "type cast foo", value_from_orig + assert_equal "type cast", value_from_clone + end + + test "duping does not dup the value if it is not dupable" do + @type.expect(:deserialize, false, ["a value"]) + attribute = Attribute.from_database(nil, "a value", @type) + + assert_same attribute.value, attribute.dup.value + end + + test "duping does not eagerly type cast if we have not yet type cast" do + attribute = Attribute.from_database(nil, "a value", @type) + attribute.dup + end + + class MyType + def cast(value) + value + " from user" + end + + def deserialize(value) + value + " from database" + end + + def assert_valid_value(*) + end + end + + test "with_value_from_user returns a new attribute with the value from the user" do + old = Attribute.from_database(nil, "old", MyType.new) + new = old.with_value_from_user("new") + + assert_equal "old from database", old.value + assert_equal "new from user", new.value + end + + test "with_value_from_database returns a new attribute with the value from the database" do + old = Attribute.from_user(nil, "old", MyType.new) + new = old.with_value_from_database("new") + + assert_equal "old from user", old.value + assert_equal "new from database", new.value + end + + test "uninitialized attributes yield their name if a block is given to value" do + block = proc { |name| name.to_s + "!" } + foo = Attribute.uninitialized(:foo, nil) + bar = Attribute.uninitialized(:bar, nil) + + assert_equal "foo!", foo.value(&block) + assert_equal "bar!", bar.value(&block) + end + + test "uninitialized attributes have no value" do + assert_nil Attribute.uninitialized(:foo, nil).value + end + + test "attributes equal other attributes with the same constructor arguments" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_database(:foo, 1, Type::Integer.new) + assert_equal first, second + end + + test "attributes do not equal attributes with different names" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_database(:bar, 1, Type::Integer.new) + assert_not_equal first, second + end + + test "attributes do not equal attributes with different types" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_database(:foo, 1, Type::Float.new) + assert_not_equal first, second + end + + test "attributes do not equal attributes with different values" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_database(:foo, 2, Type::Integer.new) + assert_not_equal first, second + end + + test "attributes do not equal attributes of other classes" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_user(:foo, 1, Type::Integer.new) + assert_not_equal first, second + end + + test "an attribute has not been read by default" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + assert_not attribute.has_been_read? + end + + test "an attribute has been read when its value is calculated" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + attribute.value + assert attribute.has_been_read? + end + + test "an attribute is not changed if it hasn't been assigned or mutated" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + + refute attribute.changed? + end + + test "an attribute is changed if it's been assigned a new value" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + changed = attribute.with_value_from_user(2) + + assert changed.changed? + end + + test "an attribute is not changed if it's assigned the same value" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + unchanged = attribute.with_value_from_user(1) + + refute unchanged.changed? + end + + test "an attribute can not be mutated if it has not been read, + and skips expensive calculations" do + type_which_raises_from_all_methods = Object.new + attribute = Attribute.from_database(:foo, "bar", type_which_raises_from_all_methods) + + assert_not attribute.changed_in_place? + end + + test "an attribute is changed if it has been mutated" do + attribute = Attribute.from_database(:foo, "bar", Type::String.new) + attribute.value << "!" + + assert attribute.changed_in_place? + assert attribute.changed? + end + + test "an attribute can forget its changes" do + attribute = Attribute.from_database(:foo, "bar", Type::String.new) + changed = attribute.with_value_from_user("foo") + forgotten = changed.forgetting_assignment + + assert changed.changed? # sanity check + refute forgotten.changed? + end + + test "with_value_from_user validates the value" do + type = Type::Value.new + type.define_singleton_method(:assert_valid_value) do |value| + if value == 1 + raise ArgumentError + end + end + + attribute = Attribute.from_database(:foo, 1, type) + assert_equal 1, attribute.value + assert_equal 2, attribute.with_value_from_user(2).value + assert_raises ArgumentError do + attribute.with_value_from_user(1) + end + end + + test "with_type preserves mutations" do + attribute = Attribute.from_database(:foo, "".dup, Type::Value.new) + attribute.value << "1" + + assert_equal 1, attribute.with_type(Type::Integer.new).value + end + end +end diff --git a/activemodel/test/cases/attributes_dirty_test.rb b/activemodel/test/cases/attributes_dirty_test.rb index 26b0e85db3..83a86371e0 100644 --- a/activemodel/test/cases/attributes_dirty_test.rb +++ b/activemodel/test/cases/attributes_dirty_test.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/attributes" class AttributesDirtyTest < ActiveModel::TestCase class DirtyModel include ActiveModel::Model include ActiveModel::Attributes + include ActiveModel::Dirty attribute :name, :string attribute :color, :string attribute :size, :integer @@ -69,12 +69,10 @@ class AttributesDirtyTest < ActiveModel::TestCase end test "attribute mutation" do - @model.instance_variable_set("@name", "Yam".dup) + @model.name = "Yam" + @model.save assert !@model.name_changed? @model.name.replace("Hadad") - assert !@model.name_changed? - @model.name_will_change! - @model.name.replace("Baal") assert @model.name_changed? end @@ -190,4 +188,18 @@ class AttributesDirtyTest < ActiveModel::TestCase assert_equal "Dmitry", @model.name assert_equal "White", @model.color end + + test "changing the attribute reports a change only when the cast value changes" do + @model.size = "2.3" + @model.save + @model.size = "2.1" + + assert_equal false, @model.changed? + + @model.size = "5.1" + + assert_equal true, @model.changed? + assert_equal true, @model.size_changed? + assert_equal({ "size" => [2, 5] }, @model.changes) + end end diff --git a/activemodel/test/cases/attributes_test.rb b/activemodel/test/cases/attributes_test.rb index 064cba40e3..914aee1ac0 100644 --- a/activemodel/test/cases/attributes_test.rb +++ b/activemodel/test/cases/attributes_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/attributes" module ActiveModel class AttributesTest < ActiveModel::TestCase @@ -13,7 +12,7 @@ module ActiveModel attribute :string_field, :string attribute :decimal_field, :decimal attribute :string_with_default, :string, default: "default string" - attribute :date_field, :string, default: -> { Date.new(2016, 1, 1) } + attribute :date_field, :date, default: -> { Date.new(2016, 1, 1) } attribute :boolean_field, :boolean end @@ -48,31 +47,6 @@ module ActiveModel assert_equal true, data.boolean_field end - test "dirty" do - data = ModelForAttributesTest.new( - integer_field: "2.3", - string_field: "Rails FTW", - decimal_field: "12.3", - boolean_field: "0" - ) - - assert_equal false, data.changed? - - data.integer_field = "2.1" - - assert_equal false, data.changed? - - data.string_with_default = "default string" - - assert_equal false, data.changed? - - data.integer_field = "5.1" - - assert_equal true, data.changed? - assert_equal true, data.integer_field_changed? - assert_equal({ "integer_field" => [2, 5] }, data.changes) - end - test "nonexistent attribute" do assert_raise ActiveModel::UnknownAttributeError do ModelForAttributesTest.new(nonexistent: "nonexistent") diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb index 2cd9e185e6..dfe041ff50 100644 --- a/activemodel/test/cases/dirty_test.rb +++ b/activemodel/test/cases/dirty_test.rb @@ -219,4 +219,8 @@ class DirtyTest < ActiveModel::TestCase assert_equal "Dmitry", @model.name assert_equal "White", @model.color end + + test "model can be dup-ed without Attributes" do + assert @model.dup + end end diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index bf6dfd46e1..0e036f05f5 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -27,14 +27,14 @@ require "active_support" require "active_support/rails" require "active_model" require "arel" +require "yaml" require "active_record/version" -require "active_record/attribute_set" +require "active_model/attribute_set" module ActiveRecord extend ActiveSupport::Autoload - autoload :Attribute autoload :Base autoload :Callbacks autoload :Core @@ -181,3 +181,7 @@ end ActiveSupport.on_load(:i18n) do I18n.load_path << File.expand_path("active_record/locale/en.yml", __dir__) end + +YAML.load_tags["!ruby/object:ActiveRecord::AttributeSet"] = "ActiveModel::AttributeSet" +YAML.load_tags["!ruby/object:ActiveRecord::Attribute::FromDatabase"] = "ActiveModel::Attribute::FromDatabase" +YAML.load_tags["!ruby/object:ActiveRecord::LazyAttributeHash"] = "ActiveModel::LazyAttributeHash" diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb deleted file mode 100644 index fc474edc15..0000000000 --- a/activerecord/lib/active_record/attribute.rb +++ /dev/null @@ -1,242 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - class Attribute # :nodoc: - class << self - def from_database(name, value, type) - FromDatabase.new(name, value, type) - end - - def from_user(name, value, type, original_attribute = nil) - FromUser.new(name, value, type, original_attribute) - end - - def with_cast_value(name, value, type) - WithCastValue.new(name, value, type) - end - - def null(name) - Null.new(name) - end - - def uninitialized(name, type) - Uninitialized.new(name, type) - end - end - - attr_reader :name, :value_before_type_cast, :type - - # This method should not be called directly. - # Use #from_database or #from_user - def initialize(name, value_before_type_cast, type, original_attribute = nil) - @name = name - @value_before_type_cast = value_before_type_cast - @type = type - @original_attribute = original_attribute - end - - def value - # `defined?` is cheaper than `||=` when we get back falsy values - @value = type_cast(value_before_type_cast) unless defined?(@value) - @value - end - - def original_value - if assigned? - original_attribute.original_value - else - type_cast(value_before_type_cast) - end - end - - def value_for_database - type.serialize(value) - end - - def changed? - changed_from_assignment? || changed_in_place? - end - - def changed_in_place? - has_been_read? && type.changed_in_place?(original_value_for_database, value) - end - - def forgetting_assignment - with_value_from_database(value_for_database) - end - - def with_value_from_user(value) - type.assert_valid_value(value) - self.class.from_user(name, value, type, original_attribute || self) - end - - def with_value_from_database(value) - self.class.from_database(name, value, type) - end - - def with_cast_value(value) - self.class.with_cast_value(name, value, type) - end - - def with_type(type) - if changed_in_place? - with_value_from_user(value).with_type(type) - else - self.class.new(name, value_before_type_cast, type, original_attribute) - end - end - - def type_cast(*) - raise NotImplementedError - end - - def initialized? - true - end - - def came_from_user? - false - end - - def has_been_read? - defined?(@value) - end - - def ==(other) - self.class == other.class && - name == other.name && - value_before_type_cast == other.value_before_type_cast && - type == other.type - end - alias eql? == - - def hash - [self.class, name, value_before_type_cast, type].hash - end - - def init_with(coder) - @name = coder["name"] - @value_before_type_cast = coder["value_before_type_cast"] - @type = coder["type"] - @original_attribute = coder["original_attribute"] - @value = coder["value"] if coder.map.key?("value") - end - - def encode_with(coder) - coder["name"] = name - coder["value_before_type_cast"] = value_before_type_cast unless value_before_type_cast.nil? - coder["type"] = type if type - coder["original_attribute"] = original_attribute if original_attribute - 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 - end - end - - def changed_from_assignment? - assigned? && type.changed?(original_value, value, value_before_type_cast) - end - - def _original_value_for_database - type.serialize(original_value) - end - - class FromDatabase < Attribute # :nodoc: - def type_cast(value) - type.deserialize(value) - end - - def _original_value_for_database - value_before_type_cast - end - end - - class FromUser < Attribute # :nodoc: - def type_cast(value) - type.cast(value) - end - - def came_from_user? - !type.value_constructed_by_mass_assignment?(value_before_type_cast) - end - end - - class WithCastValue < Attribute # :nodoc: - def type_cast(value) - value - end - - def changed_in_place? - false - end - end - - class Null < Attribute # :nodoc: - def initialize(name) - super(name, nil, Type.default_value) - end - - def type_cast(*) - nil - end - - def with_type(type) - self.class.with_cast_value(name, nil, type) - end - - def with_value_from_database(value) - raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`" - end - alias_method :with_value_from_user, :with_value_from_database - end - - class Uninitialized < Attribute # :nodoc: - UNINITIALIZED_ORIGINAL_VALUE = Object.new - - def initialize(name, type) - super(name, nil, type) - end - - def value - if block_given? - yield name - end - end - - def original_value - UNINITIALIZED_ORIGINAL_VALUE - end - - def value_for_database - end - - def initialized? - false - end - - def with_type(type) - self.class.new(name, type) - end - end - private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue - end -end diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb deleted file mode 100644 index f746960fac..0000000000 --- a/activerecord/lib/active_record/attribute/user_provided_default.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -require "active_record/attribute" - -module ActiveRecord - class Attribute # :nodoc: - class UserProvidedDefault < FromUser # :nodoc: - def initialize(name, value, type, database_default) - @user_provided_value = value - super(name, value, type, database_default) - end - - def value_before_type_cast - if user_provided_value.is_a?(Proc) - @memoized_value_before_type_cast ||= user_provided_value.call - else - @user_provided_value - end - end - - def with_type(type) - 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 - end - end -end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 79110d04f4..3de6fe566d 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "active_support/core_ext/module/attribute_accessors" -require "active_record/attribute_mutation_tracker" module ActiveRecord module AttributeMethods @@ -33,65 +32,13 @@ module ActiveRecord # reload the record and clears changed attributes. def reload(*) super.tap do + @previously_changed = ActiveSupport::HashWithIndifferentAccess.new @mutations_before_last_save = nil + @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new @mutations_from_database = nil - @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new end end - def initialize_dup(other) # :nodoc: - super - @attributes = self.class._default_attributes.map do |attr| - attr.with_value_from_user(@attributes.fetch_value(attr.name)) - end - @mutations_from_database = nil - end - - def changes_applied # :nodoc: - @mutations_before_last_save = mutations_from_database - @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new - forget_attribute_assignments - @mutations_from_database = nil - end - - def clear_changes_information # :nodoc: - @mutations_before_last_save = nil - @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new - forget_attribute_assignments - @mutations_from_database = nil - end - - def clear_attribute_changes(attr_names) # :nodoc: - super - attr_names.each do |attr_name| - clear_attribute_change(attr_name) - end - end - - def changed_attributes # :nodoc: - # This should only be set by methods which will call changed_attributes - # multiple times when it is known that the computed value cannot change. - if defined?(@cached_changed_attributes) - @cached_changed_attributes - else - super.reverse_merge(mutations_from_database.changed_values).freeze - end - end - - def changes # :nodoc: - cache_changed_attributes do - super - end - end - - def previous_changes # :nodoc: - mutations_before_last_save.changes - end - - def attribute_changed_in_place?(attr_name) # :nodoc: - mutations_from_database.changed_in_place?(attr_name) - end - # Did this attribute change when we last saved? This method can be invoked # as +saved_change_to_name?+ instead of saved_change_to_attribute?("name"). # Behaves similarly to +attribute_changed?+. This method is useful in @@ -182,26 +129,6 @@ module ActiveRecord result end - def mutations_from_database - unless defined?(@mutations_from_database) - @mutations_from_database = nil - end - @mutations_from_database ||= AttributeMutationTracker.new(@attributes) - end - - def changes_include?(attr_name) - super || mutations_from_database.changed?(attr_name) - end - - def clear_attribute_change(attr_name) - mutations_from_database.forget_change(attr_name) - end - - def attribute_will_change!(attr_name) - super - mutations_from_database.force_change(attr_name) - end - def _update_record(*) partial_writes? ? super(keys_for_partial_write) : super end @@ -213,25 +140,6 @@ module ActiveRecord def keys_for_partial_write changed_attribute_names_to_save & self.class.column_names end - - def forget_attribute_assignments - @attributes = @attributes.map(&:forgetting_assignment) - end - - def mutations_before_last_save - @mutations_before_last_save ||= NullMutationTracker.instance - end - - def cache_changed_attributes - @cached_changed_attributes = changed_attributes - yield - ensure - clear_changed_attributes_cache - end - - def clear_changed_attributes_cache - remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes) - end end end end diff --git a/activerecord/lib/active_record/attribute_mutation_tracker.rb b/activerecord/lib/active_record/attribute_mutation_tracker.rb deleted file mode 100644 index 94bf641a5d..0000000000 --- a/activerecord/lib/active_record/attribute_mutation_tracker.rb +++ /dev/null @@ -1,111 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - class AttributeMutationTracker # :nodoc: - OPTION_NOT_GIVEN = Object.new - - def initialize(attributes) - @attributes = attributes - @forced_changes = Set.new - end - - def changed_values - attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result| - if changed?(attr_name) - result[attr_name] = attributes[attr_name].original_value - end - end - end - - def changes - attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result| - change = change_to_attribute(attr_name) - if change - result[attr_name] = change - end - end - end - - def change_to_attribute(attr_name) - attr_name = attr_name.to_s - if changed?(attr_name) - [attributes[attr_name].original_value, attributes.fetch_value(attr_name)] - end - end - - def any_changes? - attr_names.any? { |attr| changed?(attr) } - end - - def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) - attr_name = attr_name.to_s - forced_changes.include?(attr_name) || - attributes[attr_name].changed? && - (OPTION_NOT_GIVEN == from || attributes[attr_name].original_value == from) && - (OPTION_NOT_GIVEN == to || attributes[attr_name].value == to) - end - - def changed_in_place?(attr_name) - attributes[attr_name.to_s].changed_in_place? - end - - def forget_change(attr_name) - attr_name = attr_name.to_s - attributes[attr_name] = attributes[attr_name].forgetting_assignment - forced_changes.delete(attr_name) - end - - def original_value(attr_name) - attributes[attr_name.to_s].original_value - end - - def force_change(attr_name) - 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 - - private - - def attr_names - attributes.keys - end - end - - class NullMutationTracker # :nodoc: - include Singleton - - def changed_values(*) - {} - end - - def changes(*) - {} - end - - def change_to_attribute(attr_name) - end - - def any_changes?(*) - false - end - - def changed?(*) - false - end - - def changed_in_place?(*) - false - end - - def forget_change(*) - end - - def original_value(*) - end - end -end diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb deleted file mode 100644 index 9785666e77..0000000000 --- a/activerecord/lib/active_record/attribute_set.rb +++ /dev/null @@ -1,113 +0,0 @@ -# frozen_string_literal: true - -require "active_record/attribute_set/builder" -require "active_record/attribute_set/yaml_encoder" - -module ActiveRecord - class AttributeSet # :nodoc: - delegate :each_value, :fetch, to: :attributes - - def initialize(attributes) - @attributes = attributes - end - - def [](name) - attributes[name] || Attribute.null(name) - end - - def []=(name, value) - attributes[name] = value - end - - def values_before_type_cast - attributes.transform_values(&:value_before_type_cast) - end - - def to_hash - initialized_attributes.transform_values(&:value) - end - alias_method :to_h, :to_hash - - def key?(name) - attributes.key?(name) && self[name].initialized? - end - - def keys - attributes.each_key.select { |name| self[name].initialized? } - end - - if defined?(JRUBY_VERSION) - # This form is significantly faster on JRuby, and this is one of our biggest hotspots. - # https://github.com/jruby/jruby/pull/2562 - def fetch_value(name, &block) - self[name].value(&block) - end - else - def fetch_value(name) - self[name].value { |n| yield n if block_given? } - end - end - - def write_from_database(name, value) - attributes[name] = self[name].with_value_from_database(value) - end - - def write_from_user(name, value) - attributes[name] = self[name].with_value_from_user(value) - end - - def write_cast_value(name, value) - attributes[name] = self[name].with_cast_value(value) - end - - def freeze - @attributes.freeze - super - end - - def deep_dup - self.class.allocate.tap do |copy| - copy.instance_variable_set(:@attributes, attributes.deep_dup) - end - end - - def initialize_dup(_) - @attributes = attributes.dup - super - end - - def initialize_clone(_) - @attributes = attributes.clone - super - end - - def reset(key) - if key?(key) - write_from_database(key, nil) - end - end - - def accessed - attributes.select { |_, attr| attr.has_been_read? }.keys - end - - def map(&block) - new_attributes = attributes.transform_values(&block) - AttributeSet.new(new_attributes) - end - - def ==(other) - attributes == other.attributes - end - - protected - - attr_reader :attributes - - private - - def initialized_attributes - attributes.select { |_, attr| attr.initialized? } - end - end -end diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb deleted file mode 100644 index 349cc7e403..0000000000 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: true - -require "active_record/attribute" - -module ActiveRecord - class AttributeSet # :nodoc: - class Builder # :nodoc: - attr_reader :types, :always_initialized, :default - - def initialize(types, always_initialized = nil, &default) - @types = types - @always_initialized = always_initialized - @default = default - end - - def build_from_database(values = {}, additional_types = {}) - if always_initialized && !values.key?(always_initialized) - values[always_initialized] = nil - end - - attributes = LazyAttributeHash.new(types, values, additional_types, &default) - AttributeSet.new(attributes) - end - end - end - - class LazyAttributeHash # :nodoc: - delegate :transform_values, :each_key, :each_value, :fetch, to: :materialize - - def initialize(types, values, additional_types, &default) - @types = types - @values = values - @additional_types = additional_types - @materialized = false - @delegate_hash = {} - @default = default || proc {} - end - - def key?(key) - delegate_hash.key?(key) || values.key?(key) || types.key?(key) - end - - def [](key) - delegate_hash[key] || assign_default_value(key) - end - - def []=(key, value) - if frozen? - raise RuntimeError, "Can't modify frozen hash" - end - delegate_hash[key] = value - end - - def deep_dup - dup.tap do |copy| - copy.instance_variable_set(:@delegate_hash, delegate_hash.transform_values(&:dup)) - end - end - - def initialize_dup(_) - @delegate_hash = Hash[delegate_hash] - super - end - - def select - keys = types.keys | values.keys | delegate_hash.keys - keys.each_with_object({}) do |key, hash| - attribute = self[key] - if yield(key, attribute) - hash[key] = attribute - end - end - end - - def ==(other) - if other.is_a?(LazyAttributeHash) - materialize == other.materialize - else - materialize == other - end - end - - def marshal_dump - materialize - end - - def marshal_load(delegate_hash) - @delegate_hash = delegate_hash - @types = {} - @values = {} - @additional_types = {} - @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 - - def materialize - unless @materialized - values.each_key { |key| self[key] } - types.each_key { |key| self[key] } - unless frozen? - @materialized = true - end - end - delegate_hash - end - - private - - def assign_default_value(name) - type = additional_types.fetch(name, types[name]) - value_present = true - value = values.fetch(name) { value_present = false } - - if value_present - delegate_hash[name] = Attribute.from_database(name, value, type) - elsif types.key?(name) - delegate_hash[name] = default.call(name) || Attribute.uninitialized(name, type) - end - end - end -end diff --git a/activerecord/lib/active_record/attribute_set/yaml_encoder.rb b/activerecord/lib/active_record/attribute_set/yaml_encoder.rb deleted file mode 100644 index 9254ce16ab..0000000000 --- a/activerecord/lib/active_record/attribute_set/yaml_encoder.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - class AttributeSet - # Attempts to do more intelligent YAML dumping of an - # ActiveRecord::AttributeSet to reduce the size of the resulting string - class YAMLEncoder # :nodoc: - def initialize(default_types) - @default_types = default_types - end - - def encode(attribute_set, coder) - coder["concise_attributes"] = attribute_set.each_value.map do |attr| - if attr.type.equal?(default_types[attr.name]) - attr.with_type(nil) - else - attr - end - end - end - - def decode(coder) - if coder["attributes"] - coder["attributes"] - else - attributes_hash = Hash[coder["concise_attributes"].map do |attr| - if attr.type.nil? - attr = attr.with_type(default_types[attr.name]) - end - [attr.name, attr] - end] - AttributeSet.new(attributes_hash) - 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 - end - end -end diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index 9224d58928..0b7c9398a8 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "active_record/attribute/user_provided_default" +require "active_model/attribute/user_provided_default" module ActiveRecord # See ActiveRecord::Attributes::ClassMethods for documentation @@ -250,14 +250,14 @@ module ActiveRecord if value == NO_DEFAULT_PROVIDED default_attribute = _default_attributes[name].with_type(type) elsif from_user - default_attribute = Attribute::UserProvidedDefault.new( + default_attribute = ActiveModel::Attribute::UserProvidedDefault.new( name, value, type, _default_attributes.fetch(name.to_s) { nil }, ) else - default_attribute = Attribute.from_database(name, value, type) + default_attribute = ActiveModel::Attribute.from_database(name, value, type) end _default_attributes[name] = default_attribute end diff --git a/activerecord/lib/active_record/legacy_yaml_adapter.rb b/activerecord/lib/active_record/legacy_yaml_adapter.rb index 23644aab8f..ffa095dd94 100644 --- a/activerecord/lib/active_record/legacy_yaml_adapter.rb +++ b/activerecord/lib/active_record/legacy_yaml_adapter.rb @@ -8,7 +8,7 @@ module ActiveRecord case coder["active_record_yaml_version"] when 1, 2 then coder else - if coder["attributes"].is_a?(AttributeSet) + if coder["attributes"].is_a?(ActiveModel::AttributeSet) Rails420.convert(klass, coder) else Rails41.convert(klass, coder) diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index bed9400f51..12ee4a4137 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -323,7 +323,7 @@ module ActiveRecord end def attributes_builder # :nodoc: - @attributes_builder ||= AttributeSet::Builder.new(attribute_types, primary_key) do |name| + @attributes_builder ||= ActiveModel::AttributeSet::Builder.new(attribute_types, primary_key) do |name| unless columns_hash.key?(name) _default_attributes[name].dup end @@ -346,7 +346,7 @@ module ActiveRecord end def yaml_encoder # :nodoc: - @yaml_encoder ||= AttributeSet::YAMLEncoder.new(attribute_types) + @yaml_encoder ||= ActiveModel::AttributeSet::YAMLEncoder.new(attribute_types) end # Returns the type of the attribute with the given name, after applying @@ -376,7 +376,7 @@ module ActiveRecord end def _default_attributes # :nodoc: - @default_attributes ||= AttributeSet.new({}) + @default_attributes ||= ActiveModel::AttributeSet.new({}) end # Returns an array of column names as strings. diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb index fad08e2613..3532f28858 100644 --- a/activerecord/lib/active_record/relation/query_attribute.rb +++ b/activerecord/lib/active_record/relation/query_attribute.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require "active_record/attribute" +require "active_model/attribute" module ActiveRecord class Relation - class QueryAttribute < Attribute # :nodoc: + class QueryAttribute < ActiveModel::Attribute # :nodoc: def type_cast(value) value end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 897ff5c8af..9c5be4ad9b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -930,7 +930,7 @@ module ActiveRecord arel.where(where_clause.ast) unless where_clause.empty? arel.having(having_clause.ast) unless having_clause.empty? if limit_value - limit_attribute = Attribute.with_cast_value( + limit_attribute = ActiveModel::Attribute.with_cast_value( "LIMIT".freeze, connection.sanitize_limit(limit_value), Type.default_value, @@ -938,7 +938,7 @@ module ActiveRecord arel.take(Arel::Nodes::BindParam.new(limit_attribute)) end if offset_value - offset_attribute = Attribute.with_cast_value( + offset_attribute = ActiveModel::Attribute.with_cast_value( "OFFSET".freeze, offset_value.to_i, Type.default_value, diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb deleted file mode 100644 index 8be77ed88f..0000000000 --- a/activerecord/test/cases/attribute_set_test.rb +++ /dev/null @@ -1,255 +0,0 @@ -# frozen_string_literal: true - -require "cases/helper" - -module ActiveRecord - class AttributeSetTest < ActiveRecord::TestCase - test "building a new set from raw attributes" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) - attributes = builder.build_from_database(foo: "1.1", bar: "2.2") - - assert_equal 1, attributes[:foo].value - assert_equal 2.2, attributes[:bar].value - assert_equal :foo, attributes[:foo].name - assert_equal :bar, attributes[:bar].name - end - - test "building with custom types" do - builder = AttributeSet::Builder.new(foo: Type::Float.new) - attributes = builder.build_from_database({ foo: "3.3", bar: "4.4" }, { bar: Type::Integer.new }) - - assert_equal 3.3, attributes[:foo].value - assert_equal 4, attributes[:bar].value - end - - test "[] returns a null object" do - builder = AttributeSet::Builder.new(foo: Type::Float.new) - attributes = builder.build_from_database(foo: "3.3") - - assert_equal "3.3", attributes[:foo].value_before_type_cast - assert_nil attributes[:bar].value_before_type_cast - assert_equal :bar, attributes[:bar].name - end - - test "duping creates a new hash, but does not dup the attributes" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new) - attributes = builder.build_from_database(foo: 1, bar: "foo") - - # Ensure the type cast value is cached - attributes[:foo].value - attributes[:bar].value - - duped = attributes.dup - duped.write_from_database(:foo, 2) - duped[:bar].value << "bar" - - assert_equal 1, attributes[:foo].value - assert_equal 2, duped[:foo].value - assert_equal "foobar", attributes[:bar].value - assert_equal "foobar", duped[:bar].value - end - - test "deep_duping creates a new hash and dups each attribute" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new) - attributes = builder.build_from_database(foo: 1, bar: "foo") - - # Ensure the type cast value is cached - attributes[:foo].value - attributes[:bar].value - - duped = attributes.deep_dup - duped.write_from_database(:foo, 2) - duped[:bar].value << "bar" - - assert_equal 1, attributes[:foo].value - assert_equal 2, duped[:foo].value - assert_equal "foo", attributes[:bar].value - assert_equal "foobar", duped[:bar].value - end - - test "freezing cloned set does not freeze original" do - attributes = AttributeSet.new({}) - clone = attributes.clone - - clone.freeze - - assert clone.frozen? - assert_not attributes.frozen? - end - - test "to_hash returns a hash of the type cast values" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) - attributes = builder.build_from_database(foo: "1.1", bar: "2.2") - - assert_equal({ foo: 1, bar: 2.2 }, attributes.to_hash) - assert_equal({ foo: 1, bar: 2.2 }, attributes.to_h) - end - - test "to_hash maintains order" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) - attributes = builder.build_from_database(foo: "2.2", bar: "3.3") - - attributes[:bar] - hash = attributes.to_h - - assert_equal [[:foo, 2], [:bar, 3.3]], hash.to_a - end - - test "values_before_type_cast" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new) - attributes = builder.build_from_database(foo: "1.1", bar: "2.2") - - assert_equal({ foo: "1.1", bar: "2.2" }, attributes.values_before_type_cast) - end - - test "known columns are built with uninitialized attributes" do - attributes = attributes_with_uninitialized_key - assert attributes[:foo].initialized? - assert_not attributes[:bar].initialized? - end - - test "uninitialized attributes are not included in the attributes hash" do - attributes = attributes_with_uninitialized_key - assert_equal({ foo: 1 }, attributes.to_hash) - end - - test "uninitialized attributes are not included in keys" do - attributes = attributes_with_uninitialized_key - assert_equal [:foo], attributes.keys - end - - test "uninitialized attributes return false for key?" do - attributes = attributes_with_uninitialized_key - assert attributes.key?(:foo) - assert_not attributes.key?(:bar) - end - - test "unknown attributes return false for key?" do - attributes = attributes_with_uninitialized_key - assert_not attributes.key?(:wibble) - end - - test "fetch_value returns the value for the given initialized attribute" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) - attributes = builder.build_from_database(foo: "1.1", bar: "2.2") - - assert_equal 1, attributes.fetch_value(:foo) - assert_equal 2.2, attributes.fetch_value(:bar) - end - - test "fetch_value returns nil for unknown attributes" do - attributes = attributes_with_uninitialized_key - assert_nil attributes.fetch_value(:wibble) { "hello" } - end - - test "fetch_value returns nil for unknown attributes when types has a default" do - types = Hash.new(Type::Value.new) - builder = AttributeSet::Builder.new(types) - attributes = builder.build_from_database - - assert_nil attributes.fetch_value(:wibble) { "hello" } - end - - test "fetch_value uses the given block for uninitialized attributes" do - attributes = attributes_with_uninitialized_key - value = attributes.fetch_value(:bar) { |n| n.to_s + "!" } - assert_equal "bar!", value - end - - test "fetch_value returns nil for uninitialized attributes if no block is given" do - attributes = attributes_with_uninitialized_key - assert_nil attributes.fetch_value(:bar) - end - - test "the primary_key is always initialized" do - builder = AttributeSet::Builder.new({ foo: Type::Integer.new }, :foo) - attributes = builder.build_from_database - - assert attributes.key?(:foo) - assert_equal [:foo], attributes.keys - assert attributes[:foo].initialized? - end - - class MyType - def cast(value) - return if value.nil? - value + " from user" - end - - def deserialize(value) - return if value.nil? - value + " from database" - end - - def assert_valid_value(*) - end - end - - test "write_from_database sets the attribute with database typecasting" do - builder = AttributeSet::Builder.new(foo: MyType.new) - attributes = builder.build_from_database - - assert_nil attributes.fetch_value(:foo) - - attributes.write_from_database(:foo, "value") - - assert_equal "value from database", attributes.fetch_value(:foo) - end - - test "write_from_user sets the attribute with user typecasting" do - builder = AttributeSet::Builder.new(foo: MyType.new) - attributes = builder.build_from_database - - assert_nil attributes.fetch_value(:foo) - - attributes.write_from_user(:foo, "value") - - assert_equal "value from user", attributes.fetch_value(:foo) - end - - def attributes_with_uninitialized_key - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) - builder.build_from_database(foo: "1.1") - end - - test "freezing doesn't prevent the set from materializing" do - builder = AttributeSet::Builder.new(foo: Type::String.new) - attributes = builder.build_from_database(foo: "1") - - attributes.freeze - assert_equal({ foo: "1" }, attributes.to_hash) - end - - test "#accessed_attributes returns only attributes which have been read" do - builder = AttributeSet::Builder.new(foo: Type::Value.new, bar: Type::Value.new) - attributes = builder.build_from_database(foo: "1", bar: "2") - - assert_equal [], attributes.accessed - - attributes.fetch_value(:foo) - - assert_equal [:foo], attributes.accessed - end - - test "#map returns a new attribute set with the changes applied" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new) - attributes = builder.build_from_database(foo: "1", bar: "2") - new_attributes = attributes.map do |attr| - attr.with_cast_value(attr.value + 1) - end - - assert_equal 2, new_attributes.fetch_value(:foo) - assert_equal 3, new_attributes.fetch_value(:bar) - end - - test "comparison for equality is correctly implemented" do - builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new) - attributes = builder.build_from_database(foo: "1", bar: "2") - attributes2 = builder.build_from_database(foo: "1", bar: "2") - attributes3 = builder.build_from_database(foo: "2", bar: "2") - - assert_equal attributes, attributes2 - assert_not_equal attributes2, attributes3 - end - end -end diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb deleted file mode 100644 index 1731e7926e..0000000000 --- a/activerecord/test/cases/attribute_test.rb +++ /dev/null @@ -1,255 +0,0 @@ -# frozen_string_literal: true - -require "cases/helper" - -module ActiveRecord - class AttributeTest < ActiveRecord::TestCase - setup do - @type = Minitest::Mock.new - end - - teardown do - assert @type.verify - end - - test "from_database + read type casts from database" do - @type.expect(:deserialize, "type cast from database", ["a value"]) - attribute = Attribute.from_database(nil, "a value", @type) - - type_cast_value = attribute.value - - assert_equal "type cast from database", type_cast_value - end - - test "from_user + read type casts from user" do - @type.expect(:cast, "type cast from user", ["a value"]) - attribute = Attribute.from_user(nil, "a value", @type) - - type_cast_value = attribute.value - - assert_equal "type cast from user", type_cast_value - end - - test "reading memoizes the value" do - @type.expect(:deserialize, "from the database", ["whatever"]) - attribute = Attribute.from_database(nil, "whatever", @type) - - type_cast_value = attribute.value - second_read = attribute.value - - assert_equal "from the database", type_cast_value - assert_same type_cast_value, second_read - end - - test "reading memoizes falsy values" do - @type.expect(:deserialize, false, ["whatever"]) - attribute = Attribute.from_database(nil, "whatever", @type) - - attribute.value - attribute.value - end - - test "read_before_typecast returns the given value" do - attribute = Attribute.from_database(nil, "raw value", @type) - - raw_value = attribute.value_before_type_cast - - assert_equal "raw value", raw_value - end - - test "from_database + read_for_database type casts to and from database" do - @type.expect(:deserialize, "read from database", ["whatever"]) - @type.expect(:serialize, "ready for database", ["read from database"]) - attribute = Attribute.from_database(nil, "whatever", @type) - - serialize = attribute.value_for_database - - assert_equal "ready for database", serialize - end - - test "from_user + read_for_database type casts from the user to the database" do - @type.expect(:cast, "read from user", ["whatever"]) - @type.expect(:serialize, "ready for database", ["read from user"]) - attribute = Attribute.from_user(nil, "whatever", @type) - - serialize = attribute.value_for_database - - assert_equal "ready for database", serialize - end - - test "duping dups the value" do - @type.expect(:deserialize, "type cast".dup, ["a value"]) - attribute = Attribute.from_database(nil, "a value", @type) - - value_from_orig = attribute.value - value_from_clone = attribute.dup.value - value_from_orig << " foo" - - assert_equal "type cast foo", value_from_orig - assert_equal "type cast", value_from_clone - end - - test "duping does not dup the value if it is not dupable" do - @type.expect(:deserialize, false, ["a value"]) - attribute = Attribute.from_database(nil, "a value", @type) - - assert_same attribute.value, attribute.dup.value - end - - test "duping does not eagerly type cast if we have not yet type cast" do - attribute = Attribute.from_database(nil, "a value", @type) - attribute.dup - end - - class MyType - def cast(value) - value + " from user" - end - - def deserialize(value) - value + " from database" - end - - def assert_valid_value(*) - end - end - - test "with_value_from_user returns a new attribute with the value from the user" do - old = Attribute.from_database(nil, "old", MyType.new) - new = old.with_value_from_user("new") - - assert_equal "old from database", old.value - assert_equal "new from user", new.value - end - - test "with_value_from_database returns a new attribute with the value from the database" do - old = Attribute.from_user(nil, "old", MyType.new) - new = old.with_value_from_database("new") - - assert_equal "old from user", old.value - assert_equal "new from database", new.value - end - - test "uninitialized attributes yield their name if a block is given to value" do - block = proc { |name| name.to_s + "!" } - foo = Attribute.uninitialized(:foo, nil) - bar = Attribute.uninitialized(:bar, nil) - - assert_equal "foo!", foo.value(&block) - assert_equal "bar!", bar.value(&block) - end - - test "uninitialized attributes have no value" do - assert_nil Attribute.uninitialized(:foo, nil).value - end - - test "attributes equal other attributes with the same constructor arguments" do - first = Attribute.from_database(:foo, 1, Type::Integer.new) - second = Attribute.from_database(:foo, 1, Type::Integer.new) - assert_equal first, second - end - - test "attributes do not equal attributes with different names" do - first = Attribute.from_database(:foo, 1, Type::Integer.new) - second = Attribute.from_database(:bar, 1, Type::Integer.new) - assert_not_equal first, second - end - - test "attributes do not equal attributes with different types" do - first = Attribute.from_database(:foo, 1, Type::Integer.new) - second = Attribute.from_database(:foo, 1, Type::Float.new) - assert_not_equal first, second - end - - test "attributes do not equal attributes with different values" do - first = Attribute.from_database(:foo, 1, Type::Integer.new) - second = Attribute.from_database(:foo, 2, Type::Integer.new) - assert_not_equal first, second - end - - test "attributes do not equal attributes of other classes" do - first = Attribute.from_database(:foo, 1, Type::Integer.new) - second = Attribute.from_user(:foo, 1, Type::Integer.new) - assert_not_equal first, second - end - - test "an attribute has not been read by default" do - attribute = Attribute.from_database(:foo, 1, Type::Value.new) - assert_not attribute.has_been_read? - end - - test "an attribute has been read when its value is calculated" do - attribute = Attribute.from_database(:foo, 1, Type::Value.new) - attribute.value - assert attribute.has_been_read? - end - - test "an attribute is not changed if it hasn't been assigned or mutated" do - attribute = Attribute.from_database(:foo, 1, Type::Value.new) - - refute attribute.changed? - end - - test "an attribute is changed if it's been assigned a new value" do - attribute = Attribute.from_database(:foo, 1, Type::Value.new) - changed = attribute.with_value_from_user(2) - - assert changed.changed? - end - - test "an attribute is not changed if it's assigned the same value" do - attribute = Attribute.from_database(:foo, 1, Type::Value.new) - unchanged = attribute.with_value_from_user(1) - - refute unchanged.changed? - end - - test "an attribute can not be mutated if it has not been read, - and skips expensive calculations" do - type_which_raises_from_all_methods = Object.new - attribute = Attribute.from_database(:foo, "bar", type_which_raises_from_all_methods) - - assert_not attribute.changed_in_place? - end - - test "an attribute is changed if it has been mutated" do - attribute = Attribute.from_database(:foo, "bar", Type::String.new) - attribute.value << "!" - - assert attribute.changed_in_place? - assert attribute.changed? - end - - test "an attribute can forget its changes" do - attribute = Attribute.from_database(:foo, "bar", Type::String.new) - changed = attribute.with_value_from_user("foo") - forgotten = changed.forgetting_assignment - - assert changed.changed? # sanity check - refute forgotten.changed? - end - - test "with_value_from_user validates the value" do - type = Type::Value.new - type.define_singleton_method(:assert_valid_value) do |value| - if value == 1 - raise ArgumentError - end - end - - attribute = Attribute.from_database(:foo, 1, type) - assert_equal 1, attribute.value - assert_equal 2, attribute.with_value_from_user(2).value - assert_raises ArgumentError do - attribute.with_value_from_user(1) - end - end - - test "with_type preserves mutations" do - attribute = Attribute.from_database(:foo, "".dup, Type::Value.new) - attribute.value << "1" - - assert_equal 1, attribute.with_type(Type::Integer.new).value - end - end -end -- cgit v1.2.3 From 8c5115f95daa86cc9e79d6ad5c1076fee0f70903 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Fri, 10 Nov 2017 10:37:45 +0900 Subject: Bump RuboCop to 0.51.0 ## Summary RuboCop 0.51.0 was released. https://github.com/bbatsov/rubocop/releases/tag/v0.51.0 And rubocop-0-51 channel is available in Code Climate. https://github.com/codeclimate/codeclimate-rubocop/issues/109 This PR will bump RuboCop to 0.51.0 and fixes the following new offenses. ```console % bundle exec rubocop Inspecting 2358 files (snip) Offenses: actionpack/lib/action_controller/metal/http_authentication.rb:251:59: C: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping. [key.strip, value.to_s.gsub(/^"|"$/, "").delete('\'')] ^^^^ activesupport/test/core_ext/load_error_test.rb:8:39: C: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping. assert_raise(LoadError) { require 'no_this_file_don\'t_exist' } ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2358 files inspected, 2 offenses detected ``` --- .codeclimate.yml | 2 +- Gemfile.lock | 2 +- actionpack/lib/action_controller/metal/http_authentication.rb | 2 +- activesupport/test/core_ext/load_error_test.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.codeclimate.yml b/.codeclimate.yml index 1f4a1bb201..1f0f067c5b 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,7 +1,7 @@ engines: rubocop: enabled: true - channel: rubocop-0-50 + channel: rubocop-0-51 ratings: paths: diff --git a/Gemfile.lock b/Gemfile.lock index d0bfe589be..86f6ed3baf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -415,7 +415,7 @@ GEM sinatra (>= 0.9.2) vegas (~> 0.1.2) retriable (3.1.1) - rubocop (0.50.0) + rubocop (0.51.0) parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 08d9b094f3..0c8132684a 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -248,7 +248,7 @@ module ActionController def decode_credentials(header) ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, "").split(",").map do |pair| key, value = pair.split("=", 2) - [key.strip, value.to_s.gsub(/^"|"$/, "").delete('\'')] + [key.strip, value.to_s.gsub(/^"|"$/, "").delete("'")] end] end diff --git a/activesupport/test/core_ext/load_error_test.rb b/activesupport/test/core_ext/load_error_test.rb index 4fdd228ff8..41b11d0c33 100644 --- a/activesupport/test/core_ext/load_error_test.rb +++ b/activesupport/test/core_ext/load_error_test.rb @@ -5,7 +5,7 @@ require "active_support/core_ext/load_error" class TestLoadError < ActiveSupport::TestCase def test_with_require - assert_raise(LoadError) { require 'no_this_file_don\'t_exist' } + assert_raise(LoadError) { require "no_this_file_don't_exist" } end def test_with_load assert_raise(LoadError) { load "nor_does_this_one" } -- cgit v1.2.3 From 07ec810ac0c1425918220921a335ae8a20f82763 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 10 Nov 2017 13:50:16 +0900 Subject: Use `-e` option to specify the environment in console command [ci skip] Passing the environment's name as a regular argument is deprecated in 48b249927375465a7102acc71c2dfb8d49af8309. --- guides/source/command_line.md | 2 +- guides/source/configuring.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 88c559921c..648645af7c 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -290,7 +290,7 @@ INFO: You can also use the alias "c" to invoke the console: `rails c`. You can specify the environment in which the `console` command should operate. ```bash -$ bin/rails console staging +$ bin/rails console -e staging ``` If you wish to test out some code without changing any data, you can do that by invoking `rails console --sandbox`. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 7a32607eb7..2d03f0a61e 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -973,7 +973,7 @@ By default Rails ships with three environments: "development", "test", and "prod Imagine you have a server which mirrors the production environment but is only used for testing. Such a server is commonly called a "staging server". To define an environment called "staging" for this server, just create a file called `config/environments/staging.rb`. Please use the contents of any existing file in `config/environments` as a starting point and make the necessary changes from there. -That environment is no different than the default ones, start a server with `rails server -e staging`, a console with `rails console staging`, `Rails.env.staging?` works, etc. +That environment is no different than the default ones, start a server with `rails server -e staging`, a console with `rails console -e staging`, `Rails.env.staging?` works, etc. ### Deploy to a subdirectory (relative url root) -- cgit v1.2.3 From 7d862ffb6265fe889635e2f8311261a4a37702f1 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 10 Nov 2017 14:02:28 +0900 Subject: Fix "warning: instance variable @attributes not initialized" --- activemodel/lib/active_model/dirty.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index ddd93e34a6..d2ebd18107 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -257,7 +257,7 @@ module ActiveModel unless defined?(@mutations_from_database) @mutations_from_database = nil end - @mutations_from_database ||= if @attributes + @mutations_from_database ||= if defined?(@attributes) ActiveModel::AttributeMutationTracker.new(@attributes) else NullMutationTracker.instance @@ -265,7 +265,7 @@ module ActiveModel end def forget_attribute_assignments - @attributes = @attributes.map(&:forgetting_assignment) if @attributes + @attributes = @attributes.map(&:forgetting_assignment) if defined?(@attributes) end def mutations_before_last_save -- cgit v1.2.3 From c59ab795eee288d0a8d6842d2115d14f58f7badd Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 10 Nov 2017 14:25:16 +0900 Subject: Add missing requires Currently, executing the test with only `attribute_set_test.rb` results in an error. ``` ./bin/test -w test/cases/attribute_set_test.rb Run options: --seed 33470 # Running: E Error: ActiveModel::AttributeSetTest#test_#map_returns_a_new_attribute_set_with_the_changes_applied: NameError: uninitialized constant ActiveModel::AttributeSetTest::AttributeSet Did you mean? ActiveModel::Attributes ActiveModel::Attribute activemodel/test/cases/attribute_set_test.rb:235:in `block in ' bin/test test/cases/attribute_set_test.rb:234 ``` Added a missing require to fix this. Also, I suspect that this is the cause of failures in CI. Ref: https://travis-ci.org/rails/rails/jobs/299994708 --- activemodel/lib/active_model/attribute.rb | 2 ++ activemodel/test/cases/attribute_set_test.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/activemodel/lib/active_model/attribute.rb b/activemodel/lib/active_model/attribute.rb index 43130c37c5..b75ff80b31 100644 --- a/activemodel/lib/active_model/attribute.rb +++ b/activemodel/lib/active_model/attribute.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "active_support/core_ext/object/duplicable" + module ActiveModel class Attribute # :nodoc: class << self diff --git a/activemodel/test/cases/attribute_set_test.rb b/activemodel/test/cases/attribute_set_test.rb index d50e6cfa7a..50484cb9ce 100644 --- a/activemodel/test/cases/attribute_set_test.rb +++ b/activemodel/test/cases/attribute_set_test.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true require "cases/helper" +require "active_model/attribute_set" +require "active_model/type" module ActiveModel class AttributeSetTest < ActiveModel::TestCase -- cgit v1.2.3 From aec2b8b363dc907aa1a48cef3d7608ffae1ba2ab Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 10 Nov 2017 15:37:31 +0900 Subject: Remove unused require This is no longer used since fd6aaaa. --- activesupport/lib/active_support/test_case.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index ee5286bd55..d1f7e6ea09 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -11,7 +11,6 @@ require "active_support/testing/isolation" require "active_support/testing/constant_lookup" require "active_support/testing/time_helpers" require "active_support/testing/file_fixtures" -require "active_support/core_ext/kernel/reporting" module ActiveSupport class TestCase < ::Minitest::Test -- cgit v1.2.3 From 5ed618e192e9788094bd92c51255dda1c4fd0eae Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 10 Nov 2017 14:04:51 +0900 Subject: Fix "warning: assigned but unused variable - name" --- activemodel/lib/active_model/attributes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb index 13cad87875..6f84748804 100644 --- a/activemodel/lib/active_model/attributes.rb +++ b/activemodel/lib/active_model/attributes.rb @@ -77,7 +77,7 @@ module ActiveModel attr_name.to_s end - @attributes.write_from_user(attr_name.to_s, value) + @attributes.write_from_user(name, value) value end -- cgit v1.2.3 From 7fbaed1947705413ed25190224cbb1769ae5a061 Mon Sep 17 00:00:00 2001 From: aycabta Date: Fri, 10 Nov 2017 17:47:59 +0900 Subject: Use new RDoc URL of Ruby core [ci skip] --- guides/source/api_documentation_guidelines.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index 2c153d3783..10b89433e7 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -16,7 +16,7 @@ RDoc ---- The [Rails API documentation](http://api.rubyonrails.org) is generated with -[RDoc](http://docs.seattlerb.org/rdoc/). To generate it, make sure you are +[RDoc](https://ruby.github.io/rdoc/). To generate it, make sure you are in the rails root directory, run `bundle install` and execute: ```bash @@ -26,9 +26,9 @@ in the rails root directory, run `bundle install` and execute: Resulting HTML files can be found in the ./doc/rdoc directory. Please consult the RDoc documentation for help with the -[markup](http://docs.seattlerb.org/rdoc/RDoc/Markup.html), +[markup](https://ruby.github.io/rdoc/RDoc/Markup.html), and also take into account these [additional -directives](http://docs.seattlerb.org/rdoc/RDoc/Parser/Ruby.html). +directives](https://ruby.github.io/rdoc/RDoc/Parser/Ruby.html). Wording ------- -- cgit v1.2.3 From 9db2ef9cb1c98876e21ab28a6df501156ed138d7 Mon Sep 17 00:00:00 2001 From: aycabta Date: Fri, 10 Nov 2017 18:08:07 +0900 Subject: Use new Rake URL of Ruby core [ci skip] --- actionview/RUNNING_UNIT_TESTS.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionview/RUNNING_UNIT_TESTS.rdoc b/actionview/RUNNING_UNIT_TESTS.rdoc index 6c4e5e983a..e99d5ca1df 100644 --- a/actionview/RUNNING_UNIT_TESTS.rdoc +++ b/actionview/RUNNING_UNIT_TESTS.rdoc @@ -4,7 +4,7 @@ The easiest way to run the unit tests is through Rake. The default task runs the entire test suite for all classes. For more information, checkout the full array of rake tasks with "rake -T" -Rake can be found at http://docs.seattlerb.org/rake/. +Rake can be found at https://ruby.github.io/rake/. == Running by hand -- cgit v1.2.3 From de40c45f2b7d4a2ba47d28e9a4967134e45df91f Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 8 Nov 2017 22:21:12 +0900 Subject: Remove useless preloader classes They are only different by one line of code which doesn't deserve a hierarchy of 7 classes. Closes #31079. [Ryuta Kamizono & Bogdan Gusiev] --- .../lib/active_record/associations/preloader.rb | 26 ++++++---------------- .../associations/preloader/association.rb | 8 ++++++- .../associations/preloader/belongs_to.rb | 10 --------- .../preloader/collection_association.rb | 16 ------------- .../associations/preloader/has_many.rb | 10 --------- .../associations/preloader/has_many_through.rb | 11 --------- .../associations/preloader/has_one.rb | 10 --------- .../associations/preloader/has_one_through.rb | 11 --------- .../associations/preloader/singular_association.rb | 15 ------------- .../associations/preloader/through_association.rb | 2 +- 10 files changed, 15 insertions(+), 104 deletions(-) delete mode 100644 activerecord/lib/active_record/associations/preloader/belongs_to.rb delete mode 100644 activerecord/lib/active_record/associations/preloader/collection_association.rb delete mode 100644 activerecord/lib/active_record/associations/preloader/has_many.rb delete mode 100644 activerecord/lib/active_record/associations/preloader/has_many_through.rb delete mode 100644 activerecord/lib/active_record/associations/preloader/has_one.rb delete mode 100644 activerecord/lib/active_record/associations/preloader/has_one_through.rb delete mode 100644 activerecord/lib/active_record/associations/preloader/singular_association.rb diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index 5a93a89d0a..e1087be9b3 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -44,16 +44,8 @@ module ActiveRecord extend ActiveSupport::Autoload eager_autoload do - autoload :Association, "active_record/associations/preloader/association" - autoload :SingularAssociation, "active_record/associations/preloader/singular_association" - autoload :CollectionAssociation, "active_record/associations/preloader/collection_association" - autoload :ThroughAssociation, "active_record/associations/preloader/through_association" - - autoload :HasMany, "active_record/associations/preloader/has_many" - autoload :HasManyThrough, "active_record/associations/preloader/has_many_through" - autoload :HasOne, "active_record/associations/preloader/has_one" - autoload :HasOneThrough, "active_record/associations/preloader/has_one_through" - autoload :BelongsTo, "active_record/associations/preloader/belongs_to" + autoload :Association, "active_record/associations/preloader/association" + autoload :ThroughAssociation, "active_record/associations/preloader/through_association" end # Eager loads the named associations for the given Active Record record(s). @@ -182,8 +174,7 @@ module ActiveRecord end # Returns a class containing the logic needed to load preload the data - # and attach it to a relation. For example +Preloader::Association+ or - # +Preloader::HasManyThrough+. The class returned implements a `run` method + # and attach it to a relation. The class returned implements a `run` method # that accepts a preloader. def preloader_for(reflection, owners) if owners.first.association(reflection.name).loaded? @@ -191,13 +182,10 @@ module ActiveRecord end reflection.check_preloadable! - case reflection.macro - when :has_many - reflection.options[:through] ? HasManyThrough : HasMany - when :has_one - reflection.options[:through] ? HasOneThrough : HasOne - when :belongs_to - BelongsTo + if reflection.options[:through] + ThroughAssociation + else + Association end end end diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 19c337dc39..735da152b7 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -42,7 +42,13 @@ module ActiveRecord end def associate_records_to_owner(owner, records) - raise NotImplementedError + association = owner.association(reflection.name) + if reflection.collection? + association.loaded! + association.target.concat(records) + else + association.target = records.first + end end def owner_keys diff --git a/activerecord/lib/active_record/associations/preloader/belongs_to.rb b/activerecord/lib/active_record/associations/preloader/belongs_to.rb deleted file mode 100644 index a8e3340b23..0000000000 --- a/activerecord/lib/active_record/associations/preloader/belongs_to.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module Associations - class Preloader - class BelongsTo < SingularAssociation #:nodoc: - end - end - end -end diff --git a/activerecord/lib/active_record/associations/preloader/collection_association.rb b/activerecord/lib/active_record/associations/preloader/collection_association.rb deleted file mode 100644 index fc2029f54a..0000000000 --- a/activerecord/lib/active_record/associations/preloader/collection_association.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module Associations - class Preloader - class CollectionAssociation < Association #:nodoc: - private - def associate_records_to_owner(owner, records) - association = owner.association(reflection.name) - association.loaded! - association.target.concat(records) - end - end - end - end -end diff --git a/activerecord/lib/active_record/associations/preloader/has_many.rb b/activerecord/lib/active_record/associations/preloader/has_many.rb deleted file mode 100644 index 72f55bc43f..0000000000 --- a/activerecord/lib/active_record/associations/preloader/has_many.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module Associations - class Preloader - class HasMany < CollectionAssociation #:nodoc: - end - end - end -end diff --git a/activerecord/lib/active_record/associations/preloader/has_many_through.rb b/activerecord/lib/active_record/associations/preloader/has_many_through.rb deleted file mode 100644 index 3e17d07a33..0000000000 --- a/activerecord/lib/active_record/associations/preloader/has_many_through.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module Associations - class Preloader - class HasManyThrough < CollectionAssociation #:nodoc: - include ThroughAssociation - end - end - end -end diff --git a/activerecord/lib/active_record/associations/preloader/has_one.rb b/activerecord/lib/active_record/associations/preloader/has_one.rb deleted file mode 100644 index e339b65fb5..0000000000 --- a/activerecord/lib/active_record/associations/preloader/has_one.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module Associations - class Preloader - class HasOne < SingularAssociation #:nodoc: - end - end - end -end diff --git a/activerecord/lib/active_record/associations/preloader/has_one_through.rb b/activerecord/lib/active_record/associations/preloader/has_one_through.rb deleted file mode 100644 index 17734d0257..0000000000 --- a/activerecord/lib/active_record/associations/preloader/has_one_through.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module Associations - class Preloader - class HasOneThrough < SingularAssociation #:nodoc: - include ThroughAssociation - end - end - end -end diff --git a/activerecord/lib/active_record/associations/preloader/singular_association.rb b/activerecord/lib/active_record/associations/preloader/singular_association.rb deleted file mode 100644 index 30a92411e3..0000000000 --- a/activerecord/lib/active_record/associations/preloader/singular_association.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module Associations - class Preloader - class SingularAssociation < Association #:nodoc: - private - def associate_records_to_owner(owner, records) - association = owner.association(reflection.name) - association.target = records.first - end - end - end - end -end diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index 762275fbad..a6b7ab80a2 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -3,7 +3,7 @@ module ActiveRecord module Associations class Preloader - module ThroughAssociation #:nodoc: + class ThroughAssociation < Association # :nodoc: def run(preloader) already_loaded = owners.first.association(through_reflection.name).loaded? through_scope = through_scope() -- cgit v1.2.3 From 1cddc91a02be8627dd266d79af1ee2675218fdfe Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 10 Nov 2017 18:52:13 +0900 Subject: Add missing requires Currently, executing the test with only `attribute_test.rb` results in an error. ``` ./bin/test -w test/cases/attribute_test.rb Run options: --seed 41205 # Running: ....E Error: ActiveModel::AttributeTest#test_attributes_do_not_equal_attributes_with_different_types: NameError: uninitialized constant ActiveModel::AttributeTest::Type rails/activemodel/test/cases/attribute_test.rb:159:in `block in ' bin/test test/cases/attribute_test.rb:158 ``` Added a missing require to fix this. --- activemodel/lib/active_model/attribute.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activemodel/lib/active_model/attribute.rb b/activemodel/lib/active_model/attribute.rb index b75ff80b31..0b669db58a 100644 --- a/activemodel/lib/active_model/attribute.rb +++ b/activemodel/lib/active_model/attribute.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/object/duplicable" +require "active_model/type" module ActiveModel class Attribute # :nodoc: -- cgit v1.2.3 From 095079abec468ac3c668d468c16563f44c5ad021 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 10 Nov 2017 21:06:13 +0900 Subject: Adjust blank lines --- railties/lib/rails/generators/rails/app/templates/Gemfile | 1 + .../rails/app/templates/config/environments/development.rb.tt | 2 +- .../generators/rails/app/templates/config/environments/test.rb.tt | 2 +- railties/lib/rails/generators/rails/app/templates/gitignore | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index ee9db83ca2..61026f5182 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -21,6 +21,7 @@ ruby <%= "'#{RUBY_VERSION}'" -%> # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' <% unless skip_active_storage? -%> + # Use ActiveStorage variant # gem 'mini_magick', '~> 4.8' <% end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index 2746aedd6f..b383228dc0 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -26,8 +26,8 @@ Rails.application.configure do config.cache_store = :null_store end - <%- unless skip_active_storage? -%> + # Store uploaded files on the local file system (see config/storage.yml for options) config.active_storage.service = :local <%- end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt index a19f490d91..ff4c89219a 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -40,8 +40,8 @@ Rails.application.configure do # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - <%- end -%> + <%- end -%> # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore index bd6b34346a..2cd8335aba 100644 --- a/railties/lib/rails/generators/rails/app/templates/gitignore +++ b/railties/lib/rails/generators/rails/app/templates/gitignore @@ -24,13 +24,13 @@ <% unless skip_active_storage? -%> # Ignore uploaded files in development /storage/* -<% end -%> +<% end -%> <% unless options.skip_yarn? -%> /node_modules /yarn-error.log -<% end -%> +<% end -%> <% unless options.api? -%> /public/assets <% end -%> -- cgit v1.2.3 From a7ef60d5207d3b227d6b7dfe21e453d3548cdefa Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 10 Nov 2017 21:18:53 +0900 Subject: :scissors: [ci skip] --- activesupport/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 780c887b49..889919855c 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -6,9 +6,9 @@ ruby/ruby@b061634 support was added for `include?` to use `cover?` for 'linear' objects. Since we have no way of making Ruby consider TWZ instances as 'linear' we have to override `Range#include?`. - + Fixes #30799. - + *Andrew White* * Fix acronym support in `humanize` -- cgit v1.2.3 From df49896a1e67feea56062639c3cf51e8e0b12a51 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 10 Nov 2017 22:22:55 +0900 Subject: Ensure `apply_join_dependency` for subqueries in `from` and `where` Fixes #21577. --- .../relation/predicate_builder/relation_handler.rb | 4 ++++ activerecord/lib/active_record/relation/query_methods.rb | 3 +++ activerecord/test/cases/relations_test.rb | 12 ++++++++++++ 3 files changed, 19 insertions(+) diff --git a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb index f51ea4fde0..c8bbfa5051 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb @@ -4,6 +4,10 @@ module ActiveRecord class PredicateBuilder class RelationHandler # :nodoc: def call(attribute, value) + if value.eager_loading? + value = value.send(:apply_join_dependency) + end + if value.select_values.empty? value = value.select(value.arel_attribute(value.klass.primary_key)) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9c5be4ad9b..34554450dd 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -963,6 +963,9 @@ module ActiveRecord name = from_clause.name case opts when Relation + if opts.eager_loading? + opts = opts.send(:apply_join_dependency) + end name ||= "subquery" opts.arel.as(name.to_s) else diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 844be0d0bf..e44533cf60 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -195,6 +195,18 @@ class RelationTest < ActiveRecord::TestCase assert_equal(relation.map(&:post_count).sort, subquery.values.sort) end + def test_finding_with_subquery_with_eager_loading_in_from + relation = Comment.includes(:post).where("posts.type": "Post") + assert_equal relation.to_a, Comment.select("*").from(relation).to_a + assert_equal relation.to_a, Comment.select("subquery.*").from(relation).to_a + assert_equal relation.to_a, Comment.select("a.*").from(relation, :a).to_a + end + + def test_finding_with_subquery_with_eager_loading_in_where + relation = Comment.includes(:post).where("posts.type": "Post") + assert_equal relation.to_a, Comment.where(id: relation).to_a + end + def test_finding_with_conditions assert_equal ["David"], Author.where(name: "David").map(&:name) assert_equal ["Mary"], Author.where(["name = ?", "Mary"]).map(&:name) -- cgit v1.2.3 From b1e068bd30a88fbcc93a835edd6dbacf1d2d251c Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 10 Nov 2017 23:33:39 +0900 Subject: Consolidate duplicated `to_ary`/`to_a` definitions in `Relation` and `CollectionProxy` --- .../lib/active_record/associations/collection_proxy.rb | 10 ++++++---- activerecord/lib/active_record/relation.rb | 3 ++- activerecord/lib/active_record/relation/delegation.rb | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 07f7303f8d..8b4a48a38c 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -988,6 +988,12 @@ module ActiveRecord load_target == other end + ## + # :method: to_ary + # + # :call-seq: + # to_ary() + # # Returns a new array of objects from the collection. If the collection # hasn't been loaded, it fetches the records from the database. # @@ -1021,10 +1027,6 @@ module ActiveRecord # # #, # # # # # ] - def to_ary - load_target.dup - end - alias_method :to_a, :to_ary def records # :nodoc: load_target diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7615fb6ee9..e2d2f45503 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -243,9 +243,10 @@ module ActiveRecord end # Converts relation objects to Array. - def to_a + def to_ary records.dup end + alias to_a to_ary def records # :nodoc: load diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 48af777b69..4863befec8 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -38,7 +38,7 @@ module ActiveRecord # may vary depending on the klass of a relation, so we create a subclass of Relation # for each different klass, and the delegations are compiled into that subclass only. - delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join, + delegate :to_xml, :encode_with, :length, :each, :uniq, :join, :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of, :to_sentence, :to_formatted_s, :as_json, :shuffle, :split, :slice, :index, :rindex, to: :records -- cgit v1.2.3 From d6e86f17732e67ecc04870af1fb9a4eaaf0f3e1b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 10 Nov 2017 23:43:11 +0900 Subject: Fix random CI failure due to non-deterministic sorting order https://travis-ci.org/rails/rails/jobs/300163487#L1974 --- activerecord/test/cases/relations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index e44533cf60..eec43ef79e 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -204,7 +204,7 @@ class RelationTest < ActiveRecord::TestCase def test_finding_with_subquery_with_eager_loading_in_where relation = Comment.includes(:post).where("posts.type": "Post") - assert_equal relation.to_a, Comment.where(id: relation).to_a + assert_equal relation.sort_by(&:id), Comment.where(id: relation).sort_by(&:id) end def test_finding_with_conditions -- cgit v1.2.3 From fc7a6c738122ba9dd6eff4ede08d67e86a3f0ea7 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 10 Nov 2017 23:48:53 +0900 Subject: Add missing require "active_support/core_ext/hash/indifferent_access" https://travis-ci.org/rails/rails/jobs/300163454#L2236 --- activemodel/lib/active_model/attribute_mutation_tracker.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activemodel/lib/active_model/attribute_mutation_tracker.rb b/activemodel/lib/active_model/attribute_mutation_tracker.rb index 9072460124..c67e1b809a 100644 --- a/activemodel/lib/active_model/attribute_mutation_tracker.rb +++ b/activemodel/lib/active_model/attribute_mutation_tracker.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "active_support/core_ext/hash/indifferent_access" + module ActiveModel class AttributeMutationTracker # :nodoc: OPTION_NOT_GIVEN = Object.new -- cgit v1.2.3 From 4528dd6327f35d3139a48cbac9c9192f2253cbad Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 11 Nov 2017 03:54:10 +0900 Subject: Relation merging should keep joining order `joins_values.partition` will break joins values order. It should be kept as user intended order. Fixes #15488. --- activerecord/lib/active_record/relation/merger.rb | 18 ++++++++---------- activerecord/test/cases/relation_test.rb | 9 +++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index ebc72d28fd..b736b21525 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -112,22 +112,20 @@ module ActiveRecord if other.klass == relation.klass relation.joins!(*other.joins_values) else - joins_dependency, rest = other.joins_values.partition do |join| + alias_tracker = nil + joins_dependency = other.joins_values.map do |join| case join when Hash, Symbol, Array - true + alias_tracker ||= other.alias_tracker + ActiveRecord::Associations::JoinDependency.new( + other.klass, other.table, join, alias_tracker + ) else - false + join end end - join_dependency = ActiveRecord::Associations::JoinDependency.new( - other.klass, other.table, joins_dependency, other.alias_tracker - ) - - relation.joins! rest - - @relation = relation.joins join_dependency + relation.joins!(*joins_dependency) end end diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index fd5985ffe7..8362722e12 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -274,6 +274,15 @@ module ActiveRecord assert_equal({ 2 => 1, 4 => 3, 5 => 1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count) end + def test_relation_merging_keeps_joining_order + authors = Author.where(id: 1) + posts = Post.joins(:author).merge(authors) + comments = Comment.joins(:post).merge(posts) + ratings = Rating.joins(:comment).merge(comments) + + assert_equal 3, ratings.count + end + class EnsureRoundTripTypeCasting < ActiveRecord::Type::Value def type :string -- cgit v1.2.3 From 24b59434e6aca9679b9f86a41cfbb1a33e3d5619 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 11 Nov 2017 06:43:54 +0900 Subject: Add missing autoload `Type` (#31123) Attribute modules (`Attribute`, `Attributes`, `AttributeSet`) uses `Type`, but referencing `Type` before the modules still fail. ``` % ./bin/test -w test/cases/attribute_test.rb -n test_with_value_from_user_validates_the_value Run options: -n test_with_value_from_user_validates_the_value --seed 31876 E Error: ActiveModel::AttributeTest#test_with_value_from_user_validates_the_value: NameError: uninitialized constant ActiveModel::AttributeTest::Type /Users/kamipo/src/github.com/rails/rails/activemodel/test/cases/attribute_test.rb:233:in `block in ' bin/test test/cases/attribute_test.rb:232 Finished in 0.002985s, 335.0479 runs/s, 335.0479 assertions/s. 1 runs, 1 assertions, 0 failures, 1 errors, 0 skips ``` Probably we need more autoloading at least `Type`. --- activemodel/lib/active_model.rb | 1 + activemodel/lib/active_model/attribute.rb | 1 - activemodel/lib/active_model/attributes.rb | 1 - activemodel/test/cases/attribute_set_test.rb | 1 - activemodel/test/cases/type/big_integer_test.rb | 1 - activemodel/test/cases/type/binary_test.rb | 1 - activemodel/test/cases/type/boolean_test.rb | 1 - activemodel/test/cases/type/date_test.rb | 1 - activemodel/test/cases/type/date_time_test.rb | 1 - activemodel/test/cases/type/decimal_test.rb | 1 - activemodel/test/cases/type/float_test.rb | 1 - activemodel/test/cases/type/immutable_string_test.rb | 1 - activemodel/test/cases/type/integer_test.rb | 1 - activemodel/test/cases/type/registry_test.rb | 1 - activemodel/test/cases/type/string_test.rb | 1 - activemodel/test/cases/type/time_test.rb | 1 - activemodel/test/cases/type/value_test.rb | 1 - activerecord/lib/active_record.rb | 1 + activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 1 - 19 files changed, 2 insertions(+), 17 deletions(-) diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index faecd6b96f..a3884206a6 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -47,6 +47,7 @@ module ActiveModel autoload :SecurePassword autoload :Serialization autoload :Translation + autoload :Type autoload :Validations autoload :Validator diff --git a/activemodel/lib/active_model/attribute.rb b/activemodel/lib/active_model/attribute.rb index 0b669db58a..b75ff80b31 100644 --- a/activemodel/lib/active_model/attribute.rb +++ b/activemodel/lib/active_model/attribute.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "active_support/core_ext/object/duplicable" -require "active_model/type" module ActiveModel class Attribute # :nodoc: diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb index 6f84748804..cac461b549 100644 --- a/activemodel/lib/active_model/attributes.rb +++ b/activemodel/lib/active_model/attributes.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "active_support/core_ext/object/deep_dup" -require "active_model/type" require "active_model/attribute_set" require "active_model/attribute/user_provided_default" diff --git a/activemodel/test/cases/attribute_set_test.rb b/activemodel/test/cases/attribute_set_test.rb index 50484cb9ce..02c44c5d45 100644 --- a/activemodel/test/cases/attribute_set_test.rb +++ b/activemodel/test/cases/attribute_set_test.rb @@ -2,7 +2,6 @@ require "cases/helper" require "active_model/attribute_set" -require "active_model/type" module ActiveModel class AttributeSetTest < ActiveModel::TestCase diff --git a/activemodel/test/cases/type/big_integer_test.rb b/activemodel/test/cases/type/big_integer_test.rb index 3d29235d52..0fa0200df4 100644 --- a/activemodel/test/cases/type/big_integer_test.rb +++ b/activemodel/test/cases/type/big_integer_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/binary_test.rb b/activemodel/test/cases/type/binary_test.rb index ef4f125a3b..3221a73e49 100644 --- a/activemodel/test/cases/type/binary_test.rb +++ b/activemodel/test/cases/type/binary_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/boolean_test.rb b/activemodel/test/cases/type/boolean_test.rb index 97b165ab48..2d33579595 100644 --- a/activemodel/test/cases/type/boolean_test.rb +++ b/activemodel/test/cases/type/boolean_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/date_test.rb b/activemodel/test/cases/type/date_test.rb index 15c40a37b7..e8cf178612 100644 --- a/activemodel/test/cases/type/date_test.rb +++ b/activemodel/test/cases/type/date_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/date_time_test.rb b/activemodel/test/cases/type/date_time_test.rb index 598ccf485e..60f62becc2 100644 --- a/activemodel/test/cases/type/date_time_test.rb +++ b/activemodel/test/cases/type/date_time_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/decimal_test.rb b/activemodel/test/cases/type/decimal_test.rb index a0acdc2736..b91d67f95f 100644 --- a/activemodel/test/cases/type/decimal_test.rb +++ b/activemodel/test/cases/type/decimal_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/float_test.rb b/activemodel/test/cases/type/float_test.rb index 46e8b34dfe..28318e06f8 100644 --- a/activemodel/test/cases/type/float_test.rb +++ b/activemodel/test/cases/type/float_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/immutable_string_test.rb b/activemodel/test/cases/type/immutable_string_test.rb index 72f5779dfb..751f753ddb 100644 --- a/activemodel/test/cases/type/immutable_string_test.rb +++ b/activemodel/test/cases/type/immutable_string_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/integer_test.rb b/activemodel/test/cases/type/integer_test.rb index d2e635b447..8c5d18c9b3 100644 --- a/activemodel/test/cases/type/integer_test.rb +++ b/activemodel/test/cases/type/integer_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" require "active_support/core_ext/numeric/time" module ActiveModel diff --git a/activemodel/test/cases/type/registry_test.rb b/activemodel/test/cases/type/registry_test.rb index f34104286c..0633ea2538 100644 --- a/activemodel/test/cases/type/registry_test.rb +++ b/activemodel/test/cases/type/registry_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/string_test.rb b/activemodel/test/cases/type/string_test.rb index d39389718b..825c8bb246 100644 --- a/activemodel/test/cases/type/string_test.rb +++ b/activemodel/test/cases/type/string_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/time_test.rb b/activemodel/test/cases/type/time_test.rb index 0bea95768d..f7102d1e97 100644 --- a/activemodel/test/cases/type/time_test.rb +++ b/activemodel/test/cases/type/time_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activemodel/test/cases/type/value_test.rb b/activemodel/test/cases/type/value_test.rb index 671343b0c8..55b5d9d584 100644 --- a/activemodel/test/cases/type/value_test.rb +++ b/activemodel/test/cases/type/value_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "cases/helper" -require "active_model/type" module ActiveModel module Type diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 0e036f05f5..5de6503144 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -104,6 +104,7 @@ module ActiveRecord autoload :Result autoload :TableMetadata + autoload :Type end module Coders diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index e3aab8dad8..345983a655 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "active_record/type" require "active_record/connection_adapters/determine_if_preparable_visitor" require "active_record/connection_adapters/schema_cache" require "active_record/connection_adapters/sql_type_metadata" -- cgit v1.2.3 From ee5cf14ab06c366b37a6339f2af7c41457b3557b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 11 Nov 2017 19:18:17 +0900 Subject: Should test actual error which is raised from the database --- .../test/cases/adapters/mysql2/transaction_test.rb | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb index 25d9f69a89..7b032fed6d 100644 --- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb +++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb @@ -62,7 +62,29 @@ module ActiveRecord test "raises TransactionTimeout when mysql raises ER_LOCK_WAIT_TIMEOUT" do assert_raises(ActiveRecord::TransactionTimeout) do - ActiveRecord::Base.connection.execute("SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = 'Testing error', MYSQL_ERRNO = 1205;") + s = Sample.create!(value: 1) + latch1 = Concurrent::CountDownLatch.new + latch2 = Concurrent::CountDownLatch.new + + thread = Thread.new do + Sample.transaction do + Sample.lock.find(s.id) + latch1.count_down + latch2.wait + end + end + + begin + Sample.transaction do + latch1.wait + Sample.connection.execute("SET innodb_lock_wait_timeout = 1") + Sample.lock.find(s.id) + end + ensure + Sample.connection.execute("SET innodb_lock_wait_timeout = DEFAULT") + latch2.count_down + thread.join + end end end end -- cgit v1.2.3 From 4a65dfcb9adae8fb12a86521c1a34b392e6084c2 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 11 Nov 2017 19:53:40 +0900 Subject: Raise `TransactionTimeout` when lock wait timeout exceeded for PG adapter Follow up of #30360. --- activerecord/CHANGELOG.md | 4 +-- .../connection_adapters/postgresql_adapter.rb | 3 +++ .../test/cases/adapters/mysql2/transaction_test.rb | 2 +- .../cases/adapters/postgresql/transaction_test.rb | 29 ++++++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index e6a41ec281..34ef5c79e1 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -144,8 +144,8 @@ *Jeremy Green* -* Add new error class `TransactionTimeout` for MySQL adapter which will be raised - when lock wait time expires. +* Add new error class `TransactionTimeout` which will be raised + when lock wait timeout exceeded. *Gabriel Courtemanche* diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 2c3c1df2a9..46863c41ab 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -391,6 +391,7 @@ module ActiveRecord UNIQUE_VIOLATION = "23505" SERIALIZATION_FAILURE = "40001" DEADLOCK_DETECTED = "40P01" + LOCK_NOT_AVAILABLE = "55P03" def translate_exception(exception, message) return exception unless exception.respond_to?(:result) @@ -410,6 +411,8 @@ module ActiveRecord SerializationFailure.new(message) when DEADLOCK_DETECTED Deadlocked.new(message) + when LOCK_NOT_AVAILABLE + TransactionTimeout.new(message) else super end diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb index 7b032fed6d..ac9a8d9dfb 100644 --- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb +++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb @@ -60,7 +60,7 @@ module ActiveRecord end end - test "raises TransactionTimeout when mysql raises ER_LOCK_WAIT_TIMEOUT" do + test "raises TransactionTimeout when lock wait timeout exceeded" do assert_raises(ActiveRecord::TransactionTimeout) do s = Sample.create!(value: 1) latch1 = Concurrent::CountDownLatch.new diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb index f56adf4a5e..b6aec8e993 100644 --- a/activerecord/test/cases/adapters/postgresql/transaction_test.rb +++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb @@ -91,6 +91,35 @@ module ActiveRecord end end + test "raises TransactionTimeout when lock wait timeout exceeded" do + skip unless ActiveRecord::Base.connection.postgresql_version >= 90300 + assert_raises(ActiveRecord::TransactionTimeout) do + s = Sample.create!(value: 1) + latch1 = Concurrent::CountDownLatch.new + latch2 = Concurrent::CountDownLatch.new + + thread = Thread.new do + Sample.transaction do + Sample.lock.find(s.id) + latch1.count_down + latch2.wait + end + end + + begin + Sample.transaction do + latch1.wait + Sample.connection.execute("SET lock_timeout = 1") + Sample.lock.find(s.id) + end + ensure + Sample.connection.execute("SET lock_timeout = DEFAULT") + latch2.count_down + thread.join + end + end + end + private def with_warning_suppression -- cgit v1.2.3 From 99782bd2b85c58a95ebd1a104c08a52487de6dd6 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 12 Nov 2017 10:54:24 +0900 Subject: Remove unused require --- activesupport/lib/active_support/encrypted_file.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/activesupport/lib/active_support/encrypted_file.rb b/activesupport/lib/active_support/encrypted_file.rb index 1c4c1cb457..3d1455fb95 100644 --- a/activesupport/lib/active_support/encrypted_file.rb +++ b/activesupport/lib/active_support/encrypted_file.rb @@ -2,8 +2,6 @@ require "pathname" require "active_support/message_encryptor" -require "active_support/core_ext/string/strip" -require "active_support/core_ext/module/delegation" module ActiveSupport class EncryptedFile -- cgit v1.2.3 From bb30f05f3858f97791f0e1530e625b7ea2c6c5e2 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 12 Nov 2017 17:32:52 +0100 Subject: Deprecate encrypted secrets in favor of credentials. Allow edits of existing encrypted secrets generated on Rails 5.1, but refer to credentials when attempting to setup. This also removes the need for any of the setup code, so the generator can be ripped out altogether. --- .../lib/rails/commands/secrets/secrets_command.rb | 19 +++--- .../encrypted_secrets_generator.rb | 72 ---------------------- railties/lib/rails/secrets.rb | 17 ----- railties/test/commands/secrets_test.rb | 49 +++++++++++---- .../generators/encrypted_secrets_generator_test.rb | 44 ------------- railties/test/secrets_test.rb | 16 ++--- 6 files changed, 50 insertions(+), 167 deletions(-) delete mode 100644 railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb delete mode 100644 railties/test/generators/encrypted_secrets_generator_test.rb diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb index c91139e33b..73a88767e2 100644 --- a/railties/lib/rails/commands/secrets/secrets_command.rb +++ b/railties/lib/rails/commands/secrets/secrets_command.rb @@ -15,7 +15,7 @@ module Rails end def setup - generator.start + deprecate_in_favor_of_credentials_and_exit end def edit @@ -42,11 +42,10 @@ module Rails rescue Rails::Secrets::MissingKeyError => error say error.message rescue Errno::ENOENT => error - raise unless error.message =~ /secrets\.yml\.enc/ - - Rails::Secrets.read_template_for_editing do |tmp_path| - system("#{ENV["EDITOR"]} #{tmp_path}") - generator.skip_secrets_file { setup } + if error.message =~ /secrets\.yml\.enc/ + deprecate_in_favor_of_credentials_and_exit + else + raise end end @@ -55,11 +54,11 @@ module Rails end private - def generator - require "rails/generators" - require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" + def deprecate_in_favor_of_credentials_and_exit + say "Encrypted secrets is deprecated in favor of credentials. Run:" + say "bin/rails credentials --help" - Rails::Generators::EncryptedSecretsGenerator + exit 1 end end end diff --git a/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb b/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb deleted file mode 100644 index 1aa7a2622a..0000000000 --- a/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -require "rails/generators/base" -require "rails/secrets" - -module Rails - module Generators - class EncryptedSecretsGenerator < Base - def add_secrets_key_file - unless File.exist?("config/secrets.yml.key") || File.exist?("config/secrets.yml.enc") - key = Rails::Secrets.generate_key - - say "Adding config/secrets.yml.key to store the encryption key: #{key}" - say "" - say "Save this in a password manager your team can access." - say "" - say "If you lose the key, no one, including you, can access any encrypted secrets." - - say "" - create_file "config/secrets.yml.key", key - say "" - end - end - - def ignore_key_file - if File.exist?(".gitignore") - unless File.read(".gitignore").include?(key_ignore) - say "Ignoring config/secrets.yml.key so it won't end up in Git history:" - say "" - append_to_file ".gitignore", key_ignore - say "" - end - else - say "IMPORTANT: Don't commit config/secrets.yml.key. Add this to your ignore file:" - say key_ignore, :on_green - say "" - end - end - - def add_encrypted_secrets_file - unless (defined?(@@skip_secrets_file) && @@skip_secrets_file) || File.exist?("config/secrets.yml.enc") - say "Adding config/secrets.yml.enc to store secrets that needs to be encrypted." - say "" - say "For now the file contains this but it's been encrypted with the generated key:" - say "" - say Secrets.template, :on_green - say "" - - Secrets.write(Secrets.template) - - say "You can edit encrypted secrets with `bin/rails secrets:edit`." - say "" - end - - say "Add this to your config/environments/production.rb:" - say "config.read_encrypted_secrets = true" - end - - def self.skip_secrets_file - @@skip_secrets_file = true - yield - ensure - @@skip_secrets_file = false - end - - private - def key_ignore - [ "", "# Ignore encrypted secrets key file.", "config/secrets.yml.key", "" ].join("\n") - end - end - end -end diff --git a/railties/lib/rails/secrets.rb b/railties/lib/rails/secrets.rb index aea72b2d01..30e3478c9b 100644 --- a/railties/lib/rails/secrets.rb +++ b/railties/lib/rails/secrets.rb @@ -32,23 +32,10 @@ module Rails end end - def generate_key - SecureRandom.hex(OpenSSL::Cipher.new(@cipher).key_len) - end - def key ENV["RAILS_MASTER_KEY"] || read_key_file || handle_missing_key end - def template - <<-end_of_template.strip_heredoc - # See `secrets.yml` for tips on generating suitable keys. - # production: - # external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289 - - end_of_template - end - def encrypt(data) encryptor.encrypt_and_sign(data) end @@ -70,10 +57,6 @@ module Rails writing(read, &block) end - def read_template_for_editing(&block) - writing(template, &block) - end - private def handle_missing_key raise MissingKeyError diff --git a/railties/test/commands/secrets_test.rb b/railties/test/commands/secrets_test.rb index 66a81826e3..6b9f284a0c 100644 --- a/railties/test/commands/secrets_test.rb +++ b/railties/test/commands/secrets_test.rb @@ -8,21 +8,38 @@ require "rails/commands/secrets/secrets_command" class Rails::Command::SecretsCommandTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation, EnvHelpers - def setup - build_app + setup :build_app + teardown :teardown_app + + test "edit without editor gives hint" do + assert_match "No $EDITOR to open decrypted secrets in", run_edit_command(editor: "") end - def teardown - teardown_app + test "encrypted secrets are deprecated when using credentials" do + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") end - test "edit without editor gives hint" do - assert_match "No $EDITOR to open decrypted secrets in", run_edit_command(editor: "") + test "encrypted secrets are deprecated when running edit without setup" do + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") + end + + test "encrypted secrets are deprecated for 5.1 config/secrets.yml apps" do + Dir.chdir(app_path) do + FileUtils.rm("config/credentials.yml.enc") + FileUtils.touch("config/secrets.yml") + + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") + end end test "edit secrets" do - # Runs setup before first edit. - assert_match(/Adding config\/secrets\.yml\.key to store the encryption key/, run_edit_command) + prevent_deprecation # Run twice to ensure encrypted secrets can be reread after first edit pass. 2.times do @@ -31,22 +48,30 @@ class Rails::Command::SecretsCommandTest < ActiveSupport::TestCase end test "show secrets" do - run_setup_command + prevent_deprecation + assert_match(/external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289/, run_show_command) end private + def prevent_deprecation + Dir.chdir(app_path) do + File.write("config/secrets.yml.key", "f731758c639da2604dfb6bf3d1025de8") + File.write("config/secrets.yml.enc", "sEB0mHxDbeP1/KdnMk00wyzPFACl9K6t0cZWn5/Mfx/YbTHvnI07vrneqHg9kaH3wOS7L6pIQteu1P077OtE4BSx/ZRc/sgQPHyWu/tXsrfHqnPNpayOF/XZqizE91JacSFItNMWpuPsp9ynbzz+7cGhoB1S4aPNIU6u0doMrzdngDbijsaAFJmsHIQh6t/QHoJx--8aMoE0PvUWmw1Iqz--ldFqnM/K0g9k17M8PKoN/Q==") + end + end + def run_edit_command(editor: "cat") switch_env("EDITOR", editor) do - rails "secrets:edit" + rails "secrets:edit", allow_failure: true end end def run_show_command - rails "secrets:show" + rails "secrets:show", allow_failure: true end def run_setup_command - rails "secrets:setup" + rails "secrets:setup", allow_failure: true end end diff --git a/railties/test/generators/encrypted_secrets_generator_test.rb b/railties/test/generators/encrypted_secrets_generator_test.rb deleted file mode 100644 index eacb5166c0..0000000000 --- a/railties/test/generators/encrypted_secrets_generator_test.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require "generators/generators_test_helper" -require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" - -class EncryptedSecretsGeneratorTest < Rails::Generators::TestCase - include GeneratorsTestHelper - - def setup - super - cd destination_root - end - - def test_generates_key_file_and_encrypted_secrets_file - run_generator - - assert_file "config/secrets.yml.key", /\w+/ - - assert File.exist?("config/secrets.yml.enc") - assert_no_match(/# production:\n# external_api_key: \w+/, IO.binread("config/secrets.yml.enc")) - assert_match(/# production:\n# external_api_key: \w+/, Rails::Secrets.read) - end - - def test_appends_to_gitignore - FileUtils.touch(".gitignore") - - run_generator - - assert_file ".gitignore", /config\/secrets.yml.key/, /(?!config\/secrets.yml.enc)/ - end - - def test_warns_when_ignore_is_missing - assert_match(/Add this to your ignore file/i, run_generator) - end - - def test_doesnt_generate_a_new_key_file_if_already_opted_in_to_encrypted_secrets - FileUtils.mkdir("config") - File.open("config/secrets.yml.enc", "w") { |f| f.puts "already secrety" } - - run_generator - - assert_no_file "config/secrets.yml.key" - end -end diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb index 888fee173a..06877bc76a 100644 --- a/railties/test/secrets_test.rb +++ b/railties/test/secrets_test.rb @@ -1,20 +1,13 @@ # frozen_string_literal: true require "isolation/abstract_unit" -require "rails/generators" -require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" require "rails/secrets" class Rails::SecretsTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation - def setup - build_app - end - - def teardown - teardown_app - end + setup :build_app + teardown :teardown_app test "setting read to false skips parsing" do run_secrets_generator do @@ -172,9 +165,8 @@ class Rails::SecretsTest < ActiveSupport::TestCase private def run_secrets_generator Dir.chdir(app_path) do - capture(:stdout) do - Rails::Generators::EncryptedSecretsGenerator.start - end + File.write("config/secrets.yml.key", "f731758c639da2604dfb6bf3d1025de8") + File.write("config/secrets.yml.enc", "sEB0mHxDbeP1/KdnMk00wyzPFACl9K6t0cZWn5/Mfx/YbTHvnI07vrneqHg9kaH3wOS7L6pIQteu1P077OtE4BSx/ZRc/sgQPHyWu/tXsrfHqnPNpayOF/XZqizE91JacSFItNMWpuPsp9ynbzz+7cGhoB1S4aPNIU6u0doMrzdngDbijsaAFJmsHIQh6t/QHoJx--8aMoE0PvUWmw1Iqz--ldFqnM/K0g9k17M8PKoN/Q==") add_to_config <<-RUBY config.read_encrypted_secrets = true -- cgit v1.2.3 From 5744d00f5d22bbf1efde180431eb3daac76007e8 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 12 Nov 2017 20:29:35 +0100 Subject: Revert displaying master key generation info on `rails new`. It's already a default for new apps, like so many others, so no need to flaunt it. --- .../commands/credentials/credentials_command.rb | 1 + .../rails/generators/rails/app/app_generator.rb | 10 ++----- .../rails/master_key/master_key_generator.rb | 32 ++++++++++++---------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb index 1ef7c1f343..e5d3d01431 100644 --- a/railties/lib/rails/commands/credentials/credentials_command.rb +++ b/railties/lib/rails/commands/credentials/credentials_command.rb @@ -53,6 +53,7 @@ module Rails def ensure_master_key_has_been_added master_key_generator.add_master_key_file + master_key_generator.ignore_master_key_file end def ensure_credentials_have_been_added diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index b7357025ef..a99037576d 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -162,20 +162,14 @@ module Rails return if options[:pretend] || options[:dummy_app] require "rails/generators/rails/master_key/master_key_generator" - - after_bundle do - Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet]).add_master_key_file - end + Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet]).add_master_key_file_silently end def credentials return if options[:pretend] || options[:dummy_app] require "rails/generators/rails/credentials/credentials_generator" - - after_bundle do - Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently - end + Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently end def database_yml diff --git a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb index 23dbed20ac..29d83f5d81 100644 --- a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb +++ b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb @@ -20,29 +20,31 @@ module Rails log "If you lose the key, no one, including you, can access anything encrypted with it." log "" - create_file MASTER_KEY_PATH, key + add_master_key_file_silently key log "" - - ignore_master_key_file end end - private - def ignore_master_key_file - if File.exist?(".gitignore") - unless File.read(".gitignore").include?(key_ignore) - log "Ignoring #{MASTER_KEY_PATH} so it won't end up in Git history:" - log "" - append_to_file ".gitignore", key_ignore - log "" - end - else - log "IMPORTANT: Don't commit #{MASTER_KEY_PATH}. Add this to your ignore file:" - log key_ignore, :on_green + def add_master_key_file_silently(key = nil) + create_file MASTER_KEY_PATH, key || ActiveSupport::EncryptedFile.generate_key + end + + def ignore_master_key_file + if File.exist?(".gitignore") + unless File.read(".gitignore").include?(key_ignore) + log "Ignoring #{MASTER_KEY_PATH} so it won't end up in Git history:" + log "" + append_to_file ".gitignore", key_ignore log "" end + else + log "IMPORTANT: Don't commit #{MASTER_KEY_PATH}. Add this to your ignore file:" + log key_ignore, :on_green + log "" end + end + private def key_ignore [ "", "# Ignore master key for decrypting credentials and more.", "/#{MASTER_KEY_PATH}", "" ].join("\n") end -- cgit v1.2.3 From 8f2490b57f488ed60fc6e0a201ccd5e66811ab51 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Mon, 13 Nov 2017 00:55:06 -0700 Subject: Action Cable: run Redis tests against a default config without a password Simplify our dev testing and CI story since we're also testing against Redis for the Active Support cache store. Directly test whether db, host, password, etc are passed through as config instead of spinning up a Redis server with a password set on it. --- .travis.yml | 2 +- .../test/subscription_adapter/redis_test.rb | 23 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e357a0ca3e..f56bffdaab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ cache: services: - memcached + - redis-server addons: postgresql: "9.6" @@ -41,7 +42,6 @@ before_script: # Decodes to e.g. `export VARIABLE=VALUE` - $(base64 --decode <<< "ZXhwb3J0IFNBVUNFX0FDQ0VTU19LRVk9YTAzNTM0M2YtZTkyMi00MGIzLWFhM2MtMDZiM2VhNjM1YzQ4") - $(base64 --decode <<< "ZXhwb3J0IFNBVUNFX1VTRVJOQU1FPXJ1YnlvbnJhaWxz") - - redis-server --bind 127.0.0.1 --port 6379 --requirepass 'password' --daemonize yes script: 'ci/travis.rb' diff --git a/actioncable/test/subscription_adapter/redis_test.rb b/actioncable/test/subscription_adapter/redis_test.rb index 69120d5753..63823d6ef0 100644 --- a/actioncable/test/subscription_adapter/redis_test.rb +++ b/actioncable/test/subscription_adapter/redis_test.rb @@ -4,12 +4,15 @@ require "test_helper" require_relative "common" require_relative "channel_prefix" +require "active_support/testing/method_call_assertions" +require "action_cable/subscription_adapter/redis" + class RedisAdapterTest < ActionCable::TestCase include CommonSubscriptionAdapterTest include ChannelPrefixTest def cable_config - { adapter: "redis", driver: "ruby", url: "redis://:password@127.0.0.1:6379/12" } + { adapter: "redis", driver: "ruby" } end end @@ -23,6 +26,22 @@ class RedisAdapterTest::AlternateConfiguration < RedisAdapterTest def cable_config alt_cable_config = super.dup alt_cable_config.delete(:url) - alt_cable_config.merge(host: "127.0.0.1", port: 6379, db: 12, password: "password") + alt_cable_config.merge(host: "127.0.0.1", port: 6379, db: 12) + end +end + +class RedisAdapterTest::Connector < ActiveSupport::TestCase + include ActiveSupport::Testing::MethodCallAssertions + + test "slices url, host, port, db, and password from config" do + config = { url: 1, host: 2, port: 3, db: 4, password: 5 } + + assert_called_with ::Redis, :new, [ config ] do + connect config.merge(other: "unrelated", stuff: "here") + end + end + + def connect(config) + ActionCable::SubscriptionAdapter::Redis.redis_connector.call(config) end end -- cgit v1.2.3 From 00f5aca3ef5de2637134c40e2e8b5d3c1d5b1a08 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 15 Oct 2017 08:10:38 +0900 Subject: Verify credentials format before saving Currently, credentials does not check the format when saving. As a result, incorrect data as yaml is also saved. If credentials is used in config files., an error will occur in credential yaml parsing before edit, and will not be able to edit it. In order to prevent this, verify the format when saving. Related: #30851 --- activesupport/lib/active_support/encrypted_configuration.rb | 8 +++++++- activesupport/test/encrypted_configuration_test.rb | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/encrypted_configuration.rb b/activesupport/lib/active_support/encrypted_configuration.rb index b403048627..c52d3869de 100644 --- a/activesupport/lib/active_support/encrypted_configuration.rb +++ b/activesupport/lib/active_support/encrypted_configuration.rb @@ -22,6 +22,12 @@ module ActiveSupport "" end + def write(contents) + deserialize(contents) + + super + end + def config @config ||= deserialize(read).deep_symbolize_keys end @@ -36,7 +42,7 @@ module ActiveSupport end def deserialize(config) - config.present? ? YAML.load(config) : {} + config.present? ? YAML.load(config, content_path) : {} end end end diff --git a/activesupport/test/encrypted_configuration_test.rb b/activesupport/test/encrypted_configuration_test.rb index 471faa8c12..0bc915be82 100644 --- a/activesupport/test/encrypted_configuration_test.rb +++ b/activesupport/test/encrypted_configuration_test.rb @@ -51,6 +51,14 @@ class EncryptedConfigurationTest < ActiveSupport::TestCase assert_equal "things", @credentials[:new] end + test "raise error when writing an invalid format value" do + assert_raise(Psych::SyntaxError) do + @credentials.change do |config_file| + config_file.write "login: *login\n username: dummy" + end + end + end + test "raises key error when accessing config via bang method" do assert_raise(KeyError) { @credentials.something! } end -- cgit v1.2.3 From 17ea5bf80809d11c337a30e4059cab8d8eb00d01 Mon Sep 17 00:00:00 2001 From: Avneet Singh Malhotra Date: Mon, 13 Nov 2017 15:48:25 +0530 Subject: Move touch method from Skipping Callbacks section to Running Callbacks section [ci skip] --- guides/source/active_record_callbacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index 53417f012e..630dafe632 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -213,6 +213,7 @@ The following methods trigger callbacks: * `save!` * `save(validate: false)` * `toggle!` +* `touch` * `update_attribute` * `update` * `update!` @@ -245,7 +246,6 @@ Just as with validations, it is also possible to skip callbacks by using the fol * `increment` * `increment_counter` * `toggle` -* `touch` * `update_column` * `update_columns` * `update_all` -- cgit v1.2.3 From a968a7609db56f56298c462aa26809588f9375de Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 13 Nov 2017 20:15:16 +0900 Subject: Add new error class `StatementTimeout` which will be raised when statement timeout exceeded (#31129) We are sometimes using The MAX_EXECUTION_TIME hint for MySQL depending on the situation. It will prevent catastrophic performance down by wrong performing queries. The new error class `StatementTimeout` will make to be easier to handle that case. https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time --- activerecord/CHANGELOG.md | 5 ++++ .../connection_adapters/abstract_mysql_adapter.rb | 3 +++ .../connection_adapters/postgresql_adapter.rb | 3 +++ activerecord/lib/active_record/errors.rb | 7 ++++-- .../test/cases/adapters/mysql2/transaction_test.rb | 29 ++++++++++++++++++++++ .../cases/adapters/postgresql/transaction_test.rb | 28 +++++++++++++++++++++ 6 files changed, 73 insertions(+), 2 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 34ef5c79e1..c95e80755d 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Add new error class `StatementTimeout` which will be raised + when statement timeout exceeded. + + *Ryuta Kamizono* + * Fix `bin/rails db:migrate` with specified `VERSION`. `bin/rails db:migrate` with empty VERSION behaves as without `VERSION`. Check a format of `VERSION`: Allow a migration version number 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 bfec6fb784..ca651ef390 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -635,6 +635,7 @@ module ActiveRecord ER_CANNOT_ADD_FOREIGN = 1215 ER_CANNOT_CREATE_TABLE = 1005 ER_LOCK_WAIT_TIMEOUT = 1205 + ER_QUERY_TIMEOUT = 3024 def translate_exception(exception, message) case error_number(exception) @@ -660,6 +661,8 @@ module ActiveRecord Deadlocked.new(message) when ER_LOCK_WAIT_TIMEOUT TransactionTimeout.new(message) + when ER_QUERY_TIMEOUT + StatementTimeout.new(message) else super end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 46863c41ab..5ce6765dd8 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -392,6 +392,7 @@ module ActiveRecord SERIALIZATION_FAILURE = "40001" DEADLOCK_DETECTED = "40P01" LOCK_NOT_AVAILABLE = "55P03" + QUERY_CANCELED = "57014" def translate_exception(exception, message) return exception unless exception.respond_to?(:result) @@ -413,6 +414,8 @@ module ActiveRecord Deadlocked.new(message) when LOCK_NOT_AVAILABLE TransactionTimeout.new(message) + when QUERY_CANCELED + StatementTimeout.new(message) else super end diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 9ef3316393..f77cd23e22 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -335,8 +335,11 @@ module ActiveRecord class IrreversibleOrderError < ActiveRecordError end - # TransactionTimeout will be raised when lock wait timeout expires. - # Wait time value is set by innodb_lock_wait_timeout. + # TransactionTimeout will be raised when lock wait timeout exceeded. class TransactionTimeout < StatementInvalid end + + # StatementTimeout will be raised when statement timeout exceeded. + class StatementTimeout < StatementInvalid + end end diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb index ac9a8d9dfb..4a3a4503de 100644 --- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb +++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb @@ -87,5 +87,34 @@ module ActiveRecord end end end + + test "raises StatementTimeout when statement timeout exceeded" do + skip unless ActiveRecord::Base.connection.show_variable("max_execution_time") + assert_raises(ActiveRecord::StatementTimeout) do + s = Sample.create!(value: 1) + latch1 = Concurrent::CountDownLatch.new + latch2 = Concurrent::CountDownLatch.new + + thread = Thread.new do + Sample.transaction do + Sample.lock.find(s.id) + latch1.count_down + latch2.wait + end + end + + begin + Sample.transaction do + latch1.wait + Sample.connection.execute("SET max_execution_time = 1") + Sample.lock.find(s.id) + end + ensure + Sample.connection.execute("SET max_execution_time = DEFAULT") + latch2.count_down + thread.join + end + end + end end end diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb index b6aec8e993..4d63bbce59 100644 --- a/activerecord/test/cases/adapters/postgresql/transaction_test.rb +++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb @@ -120,6 +120,34 @@ module ActiveRecord end end + test "raises StatementTimeout when statement timeout exceeded" do + assert_raises(ActiveRecord::StatementTimeout) do + s = Sample.create!(value: 1) + latch1 = Concurrent::CountDownLatch.new + latch2 = Concurrent::CountDownLatch.new + + thread = Thread.new do + Sample.transaction do + Sample.lock.find(s.id) + latch1.count_down + latch2.wait + end + end + + begin + Sample.transaction do + latch1.wait + Sample.connection.execute("SET statement_timeout = 1") + Sample.lock.find(s.id) + end + ensure + Sample.connection.execute("SET statement_timeout = DEFAULT") + latch2.count_down + thread.join + end + end + end + private def with_warning_suppression -- cgit v1.2.3 From 1ecdd7e8a7f671825f36217f4d96a90defc7ba88 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 13 Nov 2017 18:04:37 +0900 Subject: Return a non zero code when can not connect to redis in CI --- activejob/test/support/integration/adapters/resque.rb | 3 ++- activejob/test/support/integration/adapters/sidekiq.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/activejob/test/support/integration/adapters/resque.rb b/activejob/test/support/integration/adapters/resque.rb index 7d5174b957..cc33878cb9 100644 --- a/activejob/test/support/integration/adapters/resque.rb +++ b/activejob/test/support/integration/adapters/resque.rb @@ -7,7 +7,8 @@ module ResqueJobsManager Resque.logger = Rails.logger unless can_run? puts "Cannot run integration tests for resque. To be able to run integration tests for resque you need to install and start redis.\n" - exit + status = ENV["CI"] ? false : true + exit status end end diff --git a/activejob/test/support/integration/adapters/sidekiq.rb b/activejob/test/support/integration/adapters/sidekiq.rb index ceb7fb61f2..a92e9c4ffe 100644 --- a/activejob/test/support/integration/adapters/sidekiq.rb +++ b/activejob/test/support/integration/adapters/sidekiq.rb @@ -18,7 +18,8 @@ module SidekiqJobsManager ActiveJob::Base.queue_adapter = :sidekiq unless can_run? puts "Cannot run integration tests for sidekiq. To be able to run integration tests for sidekiq you need to install and start redis.\n" - exit + status = ENV["CI"] ? false : true + exit status end end -- cgit v1.2.3 From 8e964556e74925b48e9eb040403a9ebacff1406b Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Mon, 13 Nov 2017 18:06:34 +0900 Subject: Make sidekiq and resque integration tests work in CI Since 8f2490b, the integration test of sidekiq and resque is not working in CI. https://travis-ci.org/rails/rails/jobs/301276197#L2055 https://travis-ci.org/rails/rails/jobs/301276197#L2061 Because 8f2490b removed password from `redis-server`. So must also remove passwords from these tests. --- .travis.yml | 4 ++++ activejob/test/support/integration/adapters/resque.rb | 2 +- activejob/test/support/integration/adapters/sidekiq.rb | 8 -------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index f56bffdaab..40af3dd044 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,21 +73,25 @@ matrix: env: "GEM=aj:integration" services: - memcached + - redis-server - rabbitmq - rvm: 2.3.5 env: "GEM=aj:integration" services: - memcached + - redis-server - rabbitmq - rvm: 2.4.2 env: "GEM=aj:integration" services: - memcached + - redis-server - rabbitmq - rvm: ruby-head env: "GEM=aj:integration" services: - memcached + - redis-server - rabbitmq - rvm: 2.3.5 env: diff --git a/activejob/test/support/integration/adapters/resque.rb b/activejob/test/support/integration/adapters/resque.rb index cc33878cb9..2ed8302277 100644 --- a/activejob/test/support/integration/adapters/resque.rb +++ b/activejob/test/support/integration/adapters/resque.rb @@ -3,7 +3,7 @@ module ResqueJobsManager def setup ActiveJob::Base.queue_adapter = :resque - Resque.redis = Redis::Namespace.new "active_jobs_int_test", redis: Redis.new(url: "redis://:password@127.0.0.1:6379/12", thread_safe: true) + Resque.redis = Redis::Namespace.new "active_jobs_int_test", redis: Redis.new(url: "redis://127.0.0.1:6379/12", thread_safe: true) Resque.logger = Rails.logger unless can_run? puts "Cannot run integration tests for resque. To be able to run integration tests for resque you need to install and start redis.\n" diff --git a/activejob/test/support/integration/adapters/sidekiq.rb b/activejob/test/support/integration/adapters/sidekiq.rb index a92e9c4ffe..4b01a81ec5 100644 --- a/activejob/test/support/integration/adapters/sidekiq.rb +++ b/activejob/test/support/integration/adapters/sidekiq.rb @@ -5,14 +5,6 @@ require "sidekiq/api" require "sidekiq/testing" Sidekiq::Testing.disable! -Sidekiq.configure_server do |config| - config.redis = { url: "redis://:password@127.0.0.1:6379/12" } -end - -Sidekiq.configure_client do |config| - config.redis = { url: "redis://:password@127.0.0.1:6379/12" } -end - module SidekiqJobsManager def setup ActiveJob::Base.queue_adapter = :sidekiq -- cgit v1.2.3 From 15cb4efadb61a8813967d3c25f4adfc9a918a0c0 Mon Sep 17 00:00:00 2001 From: Alessandro Rodi Date: Fri, 10 Nov 2017 09:38:05 +0100 Subject: fix bug on added? method fix rubocop issues --- activemodel/lib/active_model/errors.rb | 10 +++++++--- activemodel/test/cases/errors_test.rb | 7 +++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 971bdd08b1..275e3f1313 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -322,9 +322,13 @@ module ActiveModel # person.errors.added? :name, :too_long # => false # person.errors.added? :name, "is too long" # => false def added?(attribute, message = :invalid, options = {}) - message = message.call if message.respond_to?(:call) - message = normalize_message(attribute, message, options) - self[attribute].include? message + if message.is_a? Symbol + self.details[attribute].map { |e| e[:error] }.include? message + else + message = message.call if message.respond_to?(:call) + message = normalize_message(attribute, message, options) + self[attribute].include? message + end end # Returns all the full error messages in an array. diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index ab18af0de1..d5c282b620 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -223,6 +223,13 @@ class ErrorsTest < ActiveModel::TestCase assert !person.errors.added?(:name) end + test "added? returns false when checking for an error by symbol and a different error with same message is present" do + I18n.backend.store_translations("en", errors: { attributes: { name: { wrong: "is wrong", used: "is wrong" } } }) + person = Person.new + person.errors.add(:name, :wrong) + assert !person.errors.added?(:name, :used) + end + test "size calculates the number of error messages" do person = Person.new person.errors.add(:name, "cannot be blank") -- cgit v1.2.3 From 6acde9578fa55ceab8ef6520bbd5aab2a860d051 Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Sun, 5 Feb 2017 16:40:03 -0500 Subject: Do not use `Arel.star` when `ignored_columns` If there are any ignored columns, we will now list out all columns we want to be returned from the database. Includes a regression test. --- .../lib/active_record/relation/query_methods.rb | 2 ++ activerecord/test/cases/base_test.rb | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 897ff5c8af..a8800e432a 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1035,6 +1035,8 @@ module ActiveRecord def build_select(arel) if select_values.any? arel.project(*arel_columns(select_values.uniq)) + elsif @klass.ignored_columns.any? + arel.project(*arel_columns(@klass.column_names)) else arel.project(table[Arel.star]) end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index f0ef522515..0ae88ee6a0 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1476,4 +1476,25 @@ class BasicsTest < ActiveRecord::TestCase assert_equal(%w(first_name last_name), Developer.ignored_columns) assert_equal(%w(first_name last_name), SymbolIgnoredDeveloper.ignored_columns) end + + test "when #reload called, ignored columns' attribute methods are not defined" do + developer = Developer.create!(name: "Developer") + refute developer.respond_to?(:first_name) + refute developer.respond_to?(:first_name=) + + developer.reload + + refute developer.respond_to?(:first_name) + refute developer.respond_to?(:first_name=) + end + + test "ignored columns not included in SELECT" do + query = Developer.all.to_sql + + # ignored column + refute query.include?("first_name") + + # regular column + assert query.include?("name") + end end -- cgit v1.2.3 From 54e101855b88b577fefd671c4bc489828ec4ab52 Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Mon, 20 Feb 2017 17:07:24 -0500 Subject: Change tests to use models which don't ignore any columns --- .../test/cases/adapters/mysql2/explain_test.rb | 22 +++++++++++----------- .../test/cases/adapters/postgresql/explain_test.rb | 16 ++++++++-------- .../test/cases/adapters/sqlite3/explain_test.rb | 22 +++++++++++----------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb index 2736f7cf0e..b8e778f0b0 100644 --- a/activerecord/test/cases/adapters/mysql2/explain_test.rb +++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb @@ -1,23 +1,23 @@ # frozen_string_literal: true require "cases/helper" -require "models/developer" -require "models/computer" +require "models/author" +require "models/post" class Mysql2ExplainTest < ActiveRecord::Mysql2TestCase - fixtures :developers + fixtures :authors def test_explain_for_one_query - explain = Developer.where(id: 1).explain - assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain - assert_match %r(developers |.* const), explain + explain = Author.where(id: 1).explain + assert_match %(EXPLAIN for: SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 1), explain + assert_match %r(authors |.* const), explain end def test_explain_with_eager_loading - explain = Developer.where(id: 1).includes(:audit_logs).explain - assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain - assert_match %r(developers |.* const), explain - assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` = 1), explain - assert_match %r(audit_logs |.* ALL), explain + explain = Author.where(id: 1).includes(:posts).explain + assert_match %(EXPLAIN for: SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 1), explain + assert_match %r(authors |.* const), explain + assert_match %(EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`author_id` = 1), explain + assert_match %r(posts |.* ALL), explain end end diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb index 16fec94ede..be525383e9 100644 --- a/activerecord/test/cases/adapters/postgresql/explain_test.rb +++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb @@ -1,22 +1,22 @@ # frozen_string_literal: true require "cases/helper" -require "models/developer" -require "models/computer" +require "models/author" +require "models/post" class PostgreSQLExplainTest < ActiveRecord::PostgreSQLTestCase - fixtures :developers + fixtures :authors def test_explain_for_one_query - explain = Developer.where(id: 1).explain - assert_match %r(EXPLAIN for: SELECT "developers"\.\* FROM "developers" WHERE "developers"\."id" = (?:\$1 \[\["id", 1\]\]|1)), explain + explain = Author.where(id: 1).explain + assert_match %r(EXPLAIN for: SELECT "authors"\.\* FROM "authors" WHERE "authors"\."id" = (?:\$1 \[\["id", 1\]\]|1)), explain assert_match %(QUERY PLAN), explain end def test_explain_with_eager_loading - explain = Developer.where(id: 1).includes(:audit_logs).explain + explain = Author.where(id: 1).includes(:posts).explain assert_match %(QUERY PLAN), explain - assert_match %r(EXPLAIN for: SELECT "developers"\.\* FROM "developers" WHERE "developers"\."id" = (?:\$1 \[\["id", 1\]\]|1)), explain - assert_match %r(EXPLAIN for: SELECT "audit_logs"\.\* FROM "audit_logs" WHERE "audit_logs"\."developer_id" = (?:\$1 \[\["developer_id", 1\]\]|1)), explain + assert_match %r(EXPLAIN for: SELECT "authors"\.\* FROM "authors" WHERE "authors"\."id" = (?:\$1 \[\["id", 1\]\]|1)), explain + assert_match %r(EXPLAIN for: SELECT "posts"\.\* FROM "posts" WHERE "posts"\."author_id" = (?:\$1 \[\["author_id", 1\]\]|1)), explain end end diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb index 3b081d34e1..b6d2ccdb53 100644 --- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb @@ -1,23 +1,23 @@ # frozen_string_literal: true require "cases/helper" -require "models/developer" -require "models/computer" +require "models/author" +require "models/post" class SQLite3ExplainTest < ActiveRecord::SQLite3TestCase - fixtures :developers + fixtures :authors def test_explain_for_one_query - explain = Developer.where(id: 1).explain - assert_match %r(EXPLAIN for: SELECT "developers"\.\* FROM "developers" WHERE "developers"\."id" = (?:\? \[\["id", 1\]\]|1)), explain - assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain) + explain = Author.where(id: 1).explain + assert_match %r(EXPLAIN for: SELECT "authors"\.\* FROM "authors" WHERE "authors"\."id" = (?:\? \[\["id", 1\]\]|1)), explain + assert_match(/(SEARCH )?TABLE authors USING (INTEGER )?PRIMARY KEY/, explain) end def test_explain_with_eager_loading - explain = Developer.where(id: 1).includes(:audit_logs).explain - assert_match %r(EXPLAIN for: SELECT "developers"\.\* FROM "developers" WHERE "developers"\."id" = (?:\? \[\["id", 1\]\]|1)), explain - assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain) - assert_match %r(EXPLAIN for: SELECT "audit_logs"\.\* FROM "audit_logs" WHERE "audit_logs"\."developer_id" = (?:\? \[\["developer_id", 1\]\]|1)), explain - assert_match(/(SCAN )?TABLE audit_logs/, explain) + explain = Author.where(id: 1).includes(:posts).explain + assert_match %r(EXPLAIN for: SELECT "authors"\.\* FROM "authors" WHERE "authors"\."id" = (?:\? \[\["id", 1\]\]|1)), explain + assert_match(/(SEARCH )?TABLE authors USING (INTEGER )?PRIMARY KEY/, explain) + assert_match %r(EXPLAIN for: SELECT "posts"\.\* FROM "posts" WHERE "posts"\."author_id" = (?:\? \[\["author_id", 1\]\]|1)), explain + assert_match(/(SCAN )?TABLE posts/, explain) end end -- cgit v1.2.3 From f8627dfee9c15b9875a8ef1b358ed341ec5ce367 Mon Sep 17 00:00:00 2001 From: Gabriel Sobrinho Date: Tue, 24 Oct 2017 23:17:10 -0200 Subject: Fix postgres ordering issue on default scoping test --- .../test/cases/scoping/default_scoping_test.rb | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 716ca29eda..fdfeabaa3b 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -120,49 +120,49 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_unscope_with_where_attributes expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.where(name: "David").unscope(where: :name).collect(&:name) - assert_equal expected, received + assert_equal expected.sort, received.sort expected_2 = Developer.order("salary DESC").collect(&:name) received_2 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope({ where: :name }, :select).collect(&:name) - assert_equal expected_2, received_2 + assert_equal expected_2.sort, received_2.sort expected_3 = Developer.order("salary DESC").collect(&:name) received_3 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope(:select, :where).collect(&:name) - assert_equal expected_3, received_3 + assert_equal expected_3.sort, received_3.sort expected_4 = Developer.order("salary DESC").collect(&:name) received_4 = DeveloperOrderedBySalary.where.not("name" => "Jamis").unscope(where: :name).collect(&:name) - assert_equal expected_4, received_4 + assert_equal expected_4.sort, received_4.sort expected_5 = Developer.order("salary DESC").collect(&:name) received_5 = DeveloperOrderedBySalary.where.not("name" => ["Jamis", "David"]).unscope(where: :name).collect(&:name) - assert_equal expected_5, received_5 + assert_equal expected_5.sort, received_5.sort expected_6 = Developer.order("salary DESC").collect(&:name) received_6 = DeveloperOrderedBySalary.where(Developer.arel_table["name"].eq("David")).unscope(where: :name).collect(&:name) - assert_equal expected_6, received_6 + assert_equal expected_6.sort, received_6.sort expected_7 = Developer.order("salary DESC").collect(&:name) received_7 = DeveloperOrderedBySalary.where(Developer.arel_table[:name].eq("David")).unscope(where: :name).collect(&:name) - assert_equal expected_7, received_7 + assert_equal expected_7.sort, received_7.sort end def test_unscope_comparison_where_clauses # unscoped for WHERE (`developers`.`id` <= 2) expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.where(id: -Float::INFINITY..2).unscope(where: :id).collect { |dev| dev.name } - assert_equal expected, received + assert_equal expected.sort, received.sort # unscoped for WHERE (`developers`.`id` < 2) expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.where(id: -Float::INFINITY...2).unscope(where: :id).collect { |dev| dev.name } - assert_equal expected, received + assert_equal expected.sort, received.sort end def test_unscope_multiple_where_clauses expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.where(name: "Jamis").where(id: 1).unscope(where: [:name, :id]).collect(&:name) - assert_equal expected, received + assert_equal expected.sort, received.sort end def test_unscope_string_where_clauses_involved @@ -172,23 +172,23 @@ class DefaultScopingTest < ActiveRecord::TestCase dev_ordered_relation = DeveloperOrderedBySalary.where(name: "Jamis").where("created_at > ?", 1.year.ago) received = dev_ordered_relation.unscope(where: [:name]).collect(&:name) - assert_equal expected, received + assert_equal expected.sort, received.sort end def test_unscope_with_grouping_attributes expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect(&:name) - assert_equal expected, received + assert_equal expected.sort, received.sort expected_2 = Developer.order("salary DESC").collect(&:name) received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect(&:name) - assert_equal expected_2, received_2 + assert_equal expected_2.sort, received_2.sort end def test_unscope_with_limit_in_query expected = Developer.order("salary DESC").collect(&:name) received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect(&:name) - assert_equal expected, received + assert_equal expected.sort, received.sort end def test_order_to_unscope_reordering @@ -472,7 +472,7 @@ class DefaultScopingTest < ActiveRecord::TestCase test "a scope can remove the condition from the default scope" do scope = DeveloperCalledJamis.david2 assert_equal 1, scope.where_clause.ast.children.length - assert_equal Developer.where(name: "David"), scope + assert_equal Developer.where(name: "David").map(&:id), scope.map(&:id) end def test_with_abstract_class_where_clause_should_not_be_duplicated -- cgit v1.2.3 From 8dd76a7a6ff1bb7105beabb8f834ca54ab1e5fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 13 Nov 2017 15:23:28 -0500 Subject: Use .tt extension to all the template files Make clear that the files are not to be run for interpreters. Fixes #23847. Fixes #30690. Closes #23878. --- .../channel/templates/application_cable/channel.rb | 4 - .../templates/application_cable/channel.rb.tt | 4 + .../templates/application_cable/connection.rb | 4 - .../templates/application_cable/connection.rb.tt | 4 + .../generators/channel/templates/assets/cable.js | 13 ---- .../channel/templates/assets/cable.js.tt | 13 ++++ .../channel/templates/assets/channel.coffee | 14 ---- .../channel/templates/assets/channel.coffee.tt | 14 ++++ .../generators/channel/templates/assets/channel.js | 18 ----- .../channel/templates/assets/channel.js.tt | 18 +++++ .../rails/generators/channel/templates/channel.rb | 16 ---- .../generators/channel/templates/channel.rb.tt | 16 ++++ .../mailer/templates/application_mailer.rb | 6 -- .../mailer/templates/application_mailer.rb.tt | 6 ++ .../rails/generators/mailer/templates/mailer.rb | 17 ----- .../rails/generators/mailer/templates/mailer.rb.tt | 17 +++++ .../generators/job/templates/application_job.rb | 9 --- .../generators/job/templates/application_job.rb.tt | 9 +++ .../lib/rails/generators/job/templates/job.rb | 9 --- .../lib/rails/generators/job/templates/job.rb.tt | 9 +++ .../templates/application_record.rb | 5 -- .../templates/application_record.rb.tt | 5 ++ .../migration/templates/create_table_migration.rb | 24 ------ .../templates/create_table_migration.rb.tt | 24 ++++++ .../active_record/migration/templates/migration.rb | 46 ------------ .../migration/templates/migration.rb.tt | 46 ++++++++++++ .../active_record/model/templates/model.rb | 13 ---- .../active_record/model/templates/model.rb.tt | 13 ++++ .../active_record/model/templates/module.rb | 7 -- .../active_record/model/templates/module.rb.tt | 7 ++ .../erb/controller/templates/view.html.erb | 2 - .../erb/controller/templates/view.html.erb.tt | 2 + .../generators/erb/mailer/templates/view.html.erb | 5 -- .../erb/mailer/templates/view.html.erb.tt | 5 ++ .../generators/erb/mailer/templates/view.text.erb | 3 - .../erb/mailer/templates/view.text.erb.tt | 3 + .../erb/scaffold/templates/_form.html.erb | 34 --------- .../erb/scaffold/templates/_form.html.erb.tt | 34 +++++++++ .../erb/scaffold/templates/edit.html.erb | 6 -- .../erb/scaffold/templates/edit.html.erb.tt | 6 ++ .../erb/scaffold/templates/index.html.erb | 31 -------- .../erb/scaffold/templates/index.html.erb.tt | 31 ++++++++ .../generators/erb/scaffold/templates/new.html.erb | 5 -- .../erb/scaffold/templates/new.html.erb.tt | 5 ++ .../erb/scaffold/templates/show.html.erb | 11 --- .../erb/scaffold/templates/show.html.erb.tt | 11 +++ .../rails/generators/rails/app/templates/Gemfile | 76 ------------------- .../generators/rails/app/templates/Gemfile.tt | 76 +++++++++++++++++++ .../rails/generators/rails/app/templates/README.md | 24 ------ .../generators/rails/app/templates/README.md.tt | 24 ++++++ .../rails/generators/rails/app/templates/Rakefile | 6 -- .../generators/rails/app/templates/Rakefile.tt | 6 ++ .../app/templates/app/assets/javascripts/cable.js | 13 ---- .../templates/app/assets/javascripts/cable.js.tt | 13 ++++ .../app/assets/stylesheets/application.css | 15 ---- .../app/assets/stylesheets/application.css.tt | 15 ++++ .../app/channels/application_cable/channel.rb | 4 - .../app/channels/application_cable/channel.rb.tt | 4 + .../app/channels/application_cable/connection.rb | 4 - .../channels/application_cable/connection.rb.tt | 4 + .../templates/app/helpers/application_helper.rb | 2 - .../templates/app/helpers/application_helper.rb.tt | 2 + .../app/templates/app/jobs/application_job.rb | 2 - .../app/templates/app/jobs/application_job.rb.tt | 2 + .../templates/app/mailers/application_mailer.rb | 4 - .../templates/app/mailers/application_mailer.rb.tt | 4 + .../app/templates/app/models/application_record.rb | 3 - .../templates/app/models/application_record.rb.tt | 3 + .../generators/rails/app/templates/bin/bundle | 2 - .../generators/rails/app/templates/bin/bundle.tt | 2 + .../rails/generators/rails/app/templates/bin/rails | 3 - .../generators/rails/app/templates/bin/rails.tt | 3 + .../rails/generators/rails/app/templates/bin/rake | 3 - .../generators/rails/app/templates/bin/rake.tt | 3 + .../rails/generators/rails/app/templates/bin/yarn | 10 --- .../generators/rails/app/templates/bin/yarn.tt | 10 +++ .../rails/generators/rails/app/templates/config.ru | 5 -- .../generators/rails/app/templates/config.ru.tt | 5 ++ .../rails/app/templates/config/application.rb | 44 ----------- .../rails/app/templates/config/application.rb.tt | 44 +++++++++++ .../generators/rails/app/templates/config/boot.rb | 4 - .../rails/app/templates/config/boot.rb.tt | 4 + .../rails/app/templates/config/cable.yml | 10 --- .../rails/app/templates/config/cable.yml.tt | 10 +++ .../app/templates/config/databases/frontbase.yml | 50 ------------- .../templates/config/databases/frontbase.yml.tt | 50 +++++++++++++ .../app/templates/config/databases/ibm_db.yml | 86 ---------------------- .../app/templates/config/databases/ibm_db.yml.tt | 86 ++++++++++++++++++++++ .../rails/app/templates/config/databases/jdbc.yml | 69 ----------------- .../app/templates/config/databases/jdbc.yml.tt | 69 +++++++++++++++++ .../app/templates/config/databases/jdbcmysql.yml | 53 ------------- .../templates/config/databases/jdbcmysql.yml.tt | 53 +++++++++++++ .../templates/config/databases/jdbcpostgresql.yml | 69 ----------------- .../config/databases/jdbcpostgresql.yml.tt | 69 +++++++++++++++++ .../app/templates/config/databases/jdbcsqlite3.yml | 24 ------ .../templates/config/databases/jdbcsqlite3.yml.tt | 24 ++++++ .../rails/app/templates/config/databases/mysql.yml | 58 --------------- .../app/templates/config/databases/mysql.yml.tt | 58 +++++++++++++++ .../app/templates/config/databases/oracle.yml | 61 --------------- .../app/templates/config/databases/oracle.yml.tt | 61 +++++++++++++++ .../app/templates/config/databases/postgresql.yml | 85 --------------------- .../templates/config/databases/postgresql.yml.tt | 85 +++++++++++++++++++++ .../app/templates/config/databases/sqlite3.yml | 25 ------- .../app/templates/config/databases/sqlite3.yml.tt | 25 +++++++ .../app/templates/config/databases/sqlserver.yml | 52 ------------- .../templates/config/databases/sqlserver.yml.tt | 52 +++++++++++++ .../rails/app/templates/config/environment.rb | 5 -- .../rails/app/templates/config/environment.rb.tt | 5 ++ .../application_controller_renderer.rb | 8 -- .../application_controller_renderer.rb.tt | 8 ++ .../config/initializers/backtrace_silencers.rb | 7 -- .../config/initializers/backtrace_silencers.rb.tt | 7 ++ .../config/initializers/cookies_serializer.rb | 5 -- .../config/initializers/cookies_serializer.rb.tt | 5 ++ .../app/templates/config/initializers/cors.rb | 16 ---- .../app/templates/config/initializers/cors.rb.tt | 16 ++++ .../initializers/filter_parameter_logging.rb | 4 - .../initializers/filter_parameter_logging.rb.tt | 4 + .../templates/config/initializers/inflections.rb | 16 ---- .../config/initializers/inflections.rb.tt | 16 ++++ .../templates/config/initializers/mime_types.rb | 4 - .../templates/config/initializers/mime_types.rb.tt | 4 + .../generators/rails/app/templates/config/puma.rb | 56 -------------- .../rails/app/templates/config/puma.rb.tt | 56 ++++++++++++++ .../rails/app/templates/config/routes.rb | 3 - .../rails/app/templates/config/routes.rb.tt | 3 + .../rails/app/templates/config/spring.rb | 6 -- .../rails/app/templates/config/spring.rb.tt | 6 ++ .../rails/app/templates/config/storage.yml | 35 --------- .../rails/app/templates/config/storage.yml.tt | 35 +++++++++ .../rails/generators/rails/app/templates/gitignore | 37 ---------- .../generators/rails/app/templates/gitignore.tt | 37 ++++++++++ .../generators/rails/app/templates/package.json | 5 -- .../generators/rails/app/templates/package.json.tt | 5 ++ .../generators/rails/app/templates/ruby-version | 1 - .../generators/rails/app/templates/ruby-version.tt | 1 + .../templates/test/application_system_test_case.rb | 5 -- .../test/application_system_test_case.rb.tt | 5 ++ .../rails/app/templates/test/test_helper.rb | 11 --- .../rails/app/templates/test/test_helper.rb.tt | 11 +++ .../rails/assets/templates/stylesheet.css | 2 +- .../rails/controller/templates/controller.rb | 13 ---- .../rails/controller/templates/controller.rb.tt | 13 ++++ .../generators/rails/helper/templates/helper.rb | 4 - .../generators/rails/helper/templates/helper.rb.tt | 4 + .../rails/plugin/templates/%name%.gemspec | 24 ------ .../rails/plugin/templates/%name%.gemspec.tt | 24 ++++++ .../generators/rails/plugin/templates/Gemfile | 48 ------------ .../generators/rails/plugin/templates/Gemfile.tt | 48 ++++++++++++ .../generators/rails/plugin/templates/MIT-LICENSE | 20 ----- .../rails/plugin/templates/MIT-LICENSE.tt | 20 +++++ .../generators/rails/plugin/templates/README.md | 28 ------- .../generators/rails/plugin/templates/README.md.tt | 28 +++++++ .../generators/rails/plugin/templates/Rakefile | 28 ------- .../generators/rails/plugin/templates/Rakefile.tt | 28 +++++++ .../rails/plugin/templates/config/routes.rb | 6 -- .../rails/plugin/templates/config/routes.rb.tt | 6 ++ .../generators/rails/plugin/templates/gitignore | 18 ----- .../generators/rails/plugin/templates/gitignore.tt | 18 +++++ .../plugin/templates/lib/%namespaced_name%.rb | 7 -- .../plugin/templates/lib/%namespaced_name%.rb.tt | 7 ++ .../templates/lib/%namespaced_name%/engine.rb | 7 -- .../templates/lib/%namespaced_name%/engine.rb.tt | 7 ++ .../templates/lib/%namespaced_name%/railtie.rb | 5 -- .../templates/lib/%namespaced_name%/railtie.rb.tt | 5 ++ .../templates/lib/%namespaced_name%/version.rb | 1 - .../templates/lib/%namespaced_name%/version.rb.tt | 1 + .../lib/tasks/%namespaced_name%_tasks.rake | 4 - .../lib/tasks/%namespaced_name%_tasks.rake.tt | 4 + .../rails/plugin/templates/rails/application.rb | 23 ------ .../rails/plugin/templates/rails/application.rb.tt | 23 ++++++ .../rails/plugin/templates/rails/boot.rb | 5 -- .../rails/plugin/templates/rails/boot.rb.tt | 5 ++ .../rails/plugin/templates/rails/dummy_manifest.js | 10 --- .../plugin/templates/rails/dummy_manifest.js.tt | 10 +++ .../plugin/templates/rails/engine_manifest.js | 6 -- .../plugin/templates/rails/engine_manifest.js.tt | 6 ++ .../rails/plugin/templates/rails/javascripts.js | 16 ---- .../rails/plugin/templates/rails/javascripts.js.tt | 16 ++++ .../rails/plugin/templates/rails/routes.rb | 3 - .../rails/plugin/templates/rails/routes.rb.tt | 3 + .../templates/test/%namespaced_name%_test.rb | 7 -- .../templates/test/%namespaced_name%_test.rb.tt | 7 ++ .../templates/test/application_system_test_case.rb | 5 -- .../test/application_system_test_case.rb.tt | 5 ++ .../templates/test/integration/navigation_test.rb | 7 -- .../test/integration/navigation_test.rb.tt | 7 ++ .../rails/plugin/templates/test/test_helper.rb | 27 ------- .../rails/plugin/templates/test/test_helper.rb.tt | 27 +++++++ .../templates/api_controller.rb | 61 --------------- .../templates/api_controller.rb.tt | 61 +++++++++++++++ .../scaffold_controller/templates/controller.rb | 68 ----------------- .../scaffold_controller/templates/controller.rb.tt | 68 +++++++++++++++++ .../rails/generators/rails/task/templates/task.rb | 8 -- .../generators/rails/task/templates/task.rb.tt | 8 ++ .../controller/templates/functional_test.rb | 23 ------ .../controller/templates/functional_test.rb.tt | 23 ++++++ .../generator/templates/generator_test.rb | 16 ---- .../generator/templates/generator_test.rb.tt | 16 ++++ .../integration/templates/integration_test.rb | 9 --- .../integration/templates/integration_test.rb.tt | 9 +++ .../generators/test_unit/job/job_generator.rb | 2 +- .../test_unit/job/templates/unit_test.rb.erb | 9 --- .../test_unit/job/templates/unit_test.rb.tt | 9 +++ .../test_unit/mailer/templates/functional_test.rb | 21 ------ .../mailer/templates/functional_test.rb.tt | 21 ++++++ .../test_unit/mailer/templates/preview.rb | 13 ---- .../test_unit/mailer/templates/preview.rb.tt | 13 ++++ .../test_unit/model/templates/fixtures.yml | 29 -------- .../test_unit/model/templates/fixtures.yml.tt | 29 ++++++++ .../test_unit/model/templates/unit_test.rb | 9 --- .../test_unit/model/templates/unit_test.rb.tt | 9 +++ .../scaffold/templates/api_functional_test.rb | 44 ----------- .../scaffold/templates/api_functional_test.rb.tt | 44 +++++++++++ .../scaffold/templates/functional_test.rb | 54 -------------- .../scaffold/templates/functional_test.rb.tt | 54 ++++++++++++++ .../test_unit/scaffold/templates/system_test.rb | 49 ------------ .../test_unit/scaffold/templates/system_test.rb.tt | 49 ++++++++++++ .../templates/application_system_test_case.rb | 5 -- .../templates/application_system_test_case.rb.tt | 5 ++ .../test_unit/system/templates/system_test.rb | 9 --- .../test_unit/system/templates/system_test.rb.tt | 9 +++ railties/test/generators/generators_test_helper.rb | 4 +- 223 files changed, 2145 insertions(+), 2145 deletions(-) delete mode 100644 actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb create mode 100644 actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb.tt delete mode 100644 actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb create mode 100644 actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb.tt delete mode 100644 actioncable/lib/rails/generators/channel/templates/assets/cable.js create mode 100644 actioncable/lib/rails/generators/channel/templates/assets/cable.js.tt delete mode 100644 actioncable/lib/rails/generators/channel/templates/assets/channel.coffee create mode 100644 actioncable/lib/rails/generators/channel/templates/assets/channel.coffee.tt delete mode 100644 actioncable/lib/rails/generators/channel/templates/assets/channel.js create mode 100644 actioncable/lib/rails/generators/channel/templates/assets/channel.js.tt delete mode 100644 actioncable/lib/rails/generators/channel/templates/channel.rb create mode 100644 actioncable/lib/rails/generators/channel/templates/channel.rb.tt delete mode 100644 actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb create mode 100644 actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb.tt delete mode 100644 actionmailer/lib/rails/generators/mailer/templates/mailer.rb create mode 100644 actionmailer/lib/rails/generators/mailer/templates/mailer.rb.tt delete mode 100644 activejob/lib/rails/generators/job/templates/application_job.rb create mode 100644 activejob/lib/rails/generators/job/templates/application_job.rb.tt delete mode 100644 activejob/lib/rails/generators/job/templates/job.rb create mode 100644 activejob/lib/rails/generators/job/templates/job.rb.tt delete mode 100644 activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb create mode 100644 activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt delete mode 100644 activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb create mode 100644 activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt delete mode 100644 activerecord/lib/rails/generators/active_record/migration/templates/migration.rb create mode 100644 activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt delete mode 100644 activerecord/lib/rails/generators/active_record/model/templates/model.rb create mode 100644 activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt delete mode 100644 activerecord/lib/rails/generators/active_record/model/templates/module.rb create mode 100644 activerecord/lib/rails/generators/active_record/model/templates/module.rb.tt delete mode 100644 railties/lib/rails/generators/erb/controller/templates/view.html.erb create mode 100644 railties/lib/rails/generators/erb/controller/templates/view.html.erb.tt delete mode 100644 railties/lib/rails/generators/erb/mailer/templates/view.html.erb create mode 100644 railties/lib/rails/generators/erb/mailer/templates/view.html.erb.tt delete mode 100644 railties/lib/rails/generators/erb/mailer/templates/view.text.erb create mode 100644 railties/lib/rails/generators/erb/mailer/templates/view.text.erb.tt delete mode 100644 railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb create mode 100644 railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt delete mode 100644 railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb create mode 100644 railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt delete mode 100644 railties/lib/rails/generators/erb/scaffold/templates/index.html.erb create mode 100644 railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt delete mode 100644 railties/lib/rails/generators/erb/scaffold/templates/new.html.erb create mode 100644 railties/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt delete mode 100644 railties/lib/rails/generators/erb/scaffold/templates/show.html.erb create mode 100644 railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/Gemfile create mode 100644 railties/lib/rails/generators/rails/app/templates/Gemfile.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/README.md create mode 100644 railties/lib/rails/generators/rails/app/templates/README.md.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/Rakefile create mode 100644 railties/lib/rails/generators/rails/app/templates/Rakefile.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js create mode 100644 railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css create mode 100644 railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/bin/bundle create mode 100644 railties/lib/rails/generators/rails/app/templates/bin/bundle.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/bin/rails create mode 100644 railties/lib/rails/generators/rails/app/templates/bin/rails.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/bin/rake create mode 100644 railties/lib/rails/generators/rails/app/templates/bin/rake.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/bin/yarn create mode 100644 railties/lib/rails/generators/rails/app/templates/bin/yarn.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config.ru create mode 100644 railties/lib/rails/generators/rails/app/templates/config.ru.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/application.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/application.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/boot.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/boot.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/cable.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/environment.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/environment.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/puma.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/routes.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/spring.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/spring.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/storage.yml create mode 100644 railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/gitignore create mode 100644 railties/lib/rails/generators/rails/app/templates/gitignore.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/package.json create mode 100644 railties/lib/rails/generators/rails/app/templates/package.json.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/ruby-version create mode 100644 railties/lib/rails/generators/rails/app/templates/ruby-version.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/test/test_helper.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt delete mode 100644 railties/lib/rails/generators/rails/controller/templates/controller.rb create mode 100644 railties/lib/rails/generators/rails/controller/templates/controller.rb.tt delete mode 100644 railties/lib/rails/generators/rails/helper/templates/helper.rb create mode 100644 railties/lib/rails/generators/rails/helper/templates/helper.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec create mode 100644 railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/Gemfile create mode 100644 railties/lib/rails/generators/rails/plugin/templates/Gemfile.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE create mode 100644 railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/README.md create mode 100644 railties/lib/rails/generators/rails/plugin/templates/README.md.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/Rakefile create mode 100644 railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/config/routes.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/config/routes.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/gitignore create mode 100644 railties/lib/rails/generators/rails/plugin/templates/gitignore.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake create mode 100644 railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/application.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js create mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js create mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js create mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt delete mode 100644 railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb create mode 100644 railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt delete mode 100644 railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb create mode 100644 railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt delete mode 100644 railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb create mode 100644 railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt delete mode 100644 railties/lib/rails/generators/rails/task/templates/task.rb create mode 100644 railties/lib/rails/generators/rails/task/templates/task.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb create mode 100644 railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb create mode 100644 railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb create mode 100644 railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb create mode 100644 railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb create mode 100644 railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/mailer/templates/preview.rb create mode 100644 railties/lib/rails/generators/test_unit/mailer/templates/preview.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/model/templates/fixtures.yml create mode 100644 railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt delete mode 100644 railties/lib/rails/generators/test_unit/model/templates/unit_test.rb create mode 100644 railties/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb create mode 100644 railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb create mode 100644 railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb create mode 100644 railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb create mode 100644 railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt delete mode 100644 railties/lib/rails/generators/test_unit/system/templates/system_test.rb create mode 100644 railties/lib/rails/generators/test_unit/system/templates/system_test.rb.tt diff --git a/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb b/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb deleted file mode 100644 index d672697283..0000000000 --- a/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Channel < ActionCable::Channel::Base - end -end diff --git a/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb.tt b/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb.tt new file mode 100644 index 0000000000..d672697283 --- /dev/null +++ b/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb.tt @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb b/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb deleted file mode 100644 index 0ff5442f47..0000000000 --- a/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Connection < ActionCable::Connection::Base - end -end diff --git a/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb.tt b/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb.tt new file mode 100644 index 0000000000..0ff5442f47 --- /dev/null +++ b/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb.tt @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/actioncable/lib/rails/generators/channel/templates/assets/cable.js b/actioncable/lib/rails/generators/channel/templates/assets/cable.js deleted file mode 100644 index 739aa5f022..0000000000 --- a/actioncable/lib/rails/generators/channel/templates/assets/cable.js +++ /dev/null @@ -1,13 +0,0 @@ -// Action Cable provides the framework to deal with WebSockets in Rails. -// You can generate new channels where WebSocket features live using the `rails generate channel` command. -// -//= require action_cable -//= require_self -//= require_tree ./channels - -(function() { - this.App || (this.App = {}); - - App.cable = ActionCable.createConsumer(); - -}).call(this); diff --git a/actioncable/lib/rails/generators/channel/templates/assets/cable.js.tt b/actioncable/lib/rails/generators/channel/templates/assets/cable.js.tt new file mode 100644 index 0000000000..739aa5f022 --- /dev/null +++ b/actioncable/lib/rails/generators/channel/templates/assets/cable.js.tt @@ -0,0 +1,13 @@ +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the `rails generate channel` command. +// +//= require action_cable +//= require_self +//= require_tree ./channels + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); diff --git a/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee b/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee deleted file mode 100644 index 5467811aba..0000000000 --- a/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee +++ /dev/null @@ -1,14 +0,0 @@ -App.<%= class_name.underscore %> = App.cable.subscriptions.create "<%= class_name %>Channel", - connected: -> - # Called when the subscription is ready for use on the server - - disconnected: -> - # Called when the subscription has been terminated by the server - - received: (data) -> - # Called when there's incoming data on the websocket for this channel -<% actions.each do |action| -%> - - <%= action %>: -> - @perform '<%= action %>' -<% end -%> diff --git a/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee.tt b/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee.tt new file mode 100644 index 0000000000..5467811aba --- /dev/null +++ b/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee.tt @@ -0,0 +1,14 @@ +App.<%= class_name.underscore %> = App.cable.subscriptions.create "<%= class_name %>Channel", + connected: -> + # Called when the subscription is ready for use on the server + + disconnected: -> + # Called when the subscription has been terminated by the server + + received: (data) -> + # Called when there's incoming data on the websocket for this channel +<% actions.each do |action| -%> + + <%= action %>: -> + @perform '<%= action %>' +<% end -%> diff --git a/actioncable/lib/rails/generators/channel/templates/assets/channel.js b/actioncable/lib/rails/generators/channel/templates/assets/channel.js deleted file mode 100644 index ab0e68b11a..0000000000 --- a/actioncable/lib/rails/generators/channel/templates/assets/channel.js +++ /dev/null @@ -1,18 +0,0 @@ -App.<%= class_name.underscore %> = App.cable.subscriptions.create("<%= class_name %>Channel", { - connected: function() { - // Called when the subscription is ready for use on the server - }, - - disconnected: function() { - // Called when the subscription has been terminated by the server - }, - - received: function(data) { - // Called when there's incoming data on the websocket for this channel - }<%= actions.any? ? ",\n" : '' %> -<% actions.each do |action| -%> - <%=action %>: function() { - return this.perform('<%= action %>'); - }<%= action == actions[-1] ? '' : ",\n" %> -<% end -%> -}); diff --git a/actioncable/lib/rails/generators/channel/templates/assets/channel.js.tt b/actioncable/lib/rails/generators/channel/templates/assets/channel.js.tt new file mode 100644 index 0000000000..ab0e68b11a --- /dev/null +++ b/actioncable/lib/rails/generators/channel/templates/assets/channel.js.tt @@ -0,0 +1,18 @@ +App.<%= class_name.underscore %> = App.cable.subscriptions.create("<%= class_name %>Channel", { + connected: function() { + // Called when the subscription is ready for use on the server + }, + + disconnected: function() { + // Called when the subscription has been terminated by the server + }, + + received: function(data) { + // Called when there's incoming data on the websocket for this channel + }<%= actions.any? ? ",\n" : '' %> +<% actions.each do |action| -%> + <%=action %>: function() { + return this.perform('<%= action %>'); + }<%= action == actions[-1] ? '' : ",\n" %> +<% end -%> +}); diff --git a/actioncable/lib/rails/generators/channel/templates/channel.rb b/actioncable/lib/rails/generators/channel/templates/channel.rb deleted file mode 100644 index 4bcfb2be4d..0000000000 --- a/actioncable/lib/rails/generators/channel/templates/channel.rb +++ /dev/null @@ -1,16 +0,0 @@ -<% module_namespacing do -%> -class <%= class_name %>Channel < ApplicationCable::Channel - def subscribed - # stream_from "some_channel" - end - - def unsubscribed - # Any cleanup needed when channel is unsubscribed - end -<% actions.each do |action| -%> - - def <%= action %> - end -<% end -%> -end -<% end -%> diff --git a/actioncable/lib/rails/generators/channel/templates/channel.rb.tt b/actioncable/lib/rails/generators/channel/templates/channel.rb.tt new file mode 100644 index 0000000000..4bcfb2be4d --- /dev/null +++ b/actioncable/lib/rails/generators/channel/templates/channel.rb.tt @@ -0,0 +1,16 @@ +<% module_namespacing do -%> +class <%= class_name %>Channel < ApplicationCable::Channel + def subscribed + # stream_from "some_channel" + end + + def unsubscribed + # Any cleanup needed when channel is unsubscribed + end +<% actions.each do |action| -%> + + def <%= action %> + end +<% end -%> +end +<% end -%> diff --git a/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb b/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb deleted file mode 100644 index 00fb9bd48f..0000000000 --- a/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -<% module_namespacing do -%> -class ApplicationMailer < ActionMailer::Base - default from: 'from@example.com' - layout 'mailer' -end -<% end %> diff --git a/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb.tt b/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb.tt new file mode 100644 index 0000000000..00fb9bd48f --- /dev/null +++ b/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb.tt @@ -0,0 +1,6 @@ +<% module_namespacing do -%> +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end +<% end %> diff --git a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb deleted file mode 100644 index 348d314758..0000000000 --- a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb +++ /dev/null @@ -1,17 +0,0 @@ -<% module_namespacing do -%> -class <%= class_name %>Mailer < ApplicationMailer -<% actions.each do |action| -%> - - # Subject can be set in your I18n file at config/locales/en.yml - # with the following lookup: - # - # en.<%= file_path.tr("/",".") %>_mailer.<%= action %>.subject - # - def <%= action %> - @greeting = "Hi" - - mail to: "to@example.org" - end -<% end -%> -end -<% end -%> diff --git a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb.tt b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb.tt new file mode 100644 index 0000000000..348d314758 --- /dev/null +++ b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb.tt @@ -0,0 +1,17 @@ +<% module_namespacing do -%> +class <%= class_name %>Mailer < ApplicationMailer +<% actions.each do |action| -%> + + # Subject can be set in your I18n file at config/locales/en.yml + # with the following lookup: + # + # en.<%= file_path.tr("/",".") %>_mailer.<%= action %>.subject + # + def <%= action %> + @greeting = "Hi" + + mail to: "to@example.org" + end +<% end -%> +end +<% end -%> diff --git a/activejob/lib/rails/generators/job/templates/application_job.rb b/activejob/lib/rails/generators/job/templates/application_job.rb deleted file mode 100644 index f93745a31a..0000000000 --- a/activejob/lib/rails/generators/job/templates/application_job.rb +++ /dev/null @@ -1,9 +0,0 @@ -<% module_namespacing do -%> -class ApplicationJob < ActiveJob::Base - # Automatically retry jobs that encountered a deadlock - # retry_on ActiveRecord::Deadlocked - - # Most jobs are safe to ignore if the underlying records are no longer available - # discard_on ActiveJob::DeserializationError -end -<% end -%> diff --git a/activejob/lib/rails/generators/job/templates/application_job.rb.tt b/activejob/lib/rails/generators/job/templates/application_job.rb.tt new file mode 100644 index 0000000000..f93745a31a --- /dev/null +++ b/activejob/lib/rails/generators/job/templates/application_job.rb.tt @@ -0,0 +1,9 @@ +<% module_namespacing do -%> +class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError +end +<% end -%> diff --git a/activejob/lib/rails/generators/job/templates/job.rb b/activejob/lib/rails/generators/job/templates/job.rb deleted file mode 100644 index 4ad2914a45..0000000000 --- a/activejob/lib/rails/generators/job/templates/job.rb +++ /dev/null @@ -1,9 +0,0 @@ -<% module_namespacing do -%> -class <%= class_name %>Job < ApplicationJob - queue_as :<%= options[:queue] %> - - def perform(*args) - # Do something later - end -end -<% end -%> diff --git a/activejob/lib/rails/generators/job/templates/job.rb.tt b/activejob/lib/rails/generators/job/templates/job.rb.tt new file mode 100644 index 0000000000..4ad2914a45 --- /dev/null +++ b/activejob/lib/rails/generators/job/templates/job.rb.tt @@ -0,0 +1,9 @@ +<% module_namespacing do -%> +class <%= class_name %>Job < ApplicationJob + queue_as :<%= options[:queue] %> + + def perform(*args) + # Do something later + end +end +<% end -%> diff --git a/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb b/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb deleted file mode 100644 index 60050e0bf8..0000000000 --- a/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb +++ /dev/null @@ -1,5 +0,0 @@ -<% module_namespacing do -%> -class ApplicationRecord < ActiveRecord::Base - self.abstract_class = true -end -<% end -%> diff --git a/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt b/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt new file mode 100644 index 0000000000..60050e0bf8 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt @@ -0,0 +1,5 @@ +<% module_namespacing do -%> +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end +<% end -%> diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb deleted file mode 100644 index 5f7201cfe1..0000000000 --- a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +++ /dev/null @@ -1,24 +0,0 @@ -class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] - def change - create_table :<%= table_name %><%= primary_key_type %> do |t| -<% attributes.each do |attribute| -%> -<% if attribute.password_digest? -%> - t.string :password_digest<%= attribute.inject_options %> -<% elsif attribute.token? -%> - t.string :<%= attribute.name %><%= attribute.inject_options %> -<% else -%> - t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %> -<% end -%> -<% end -%> -<% if options[:timestamps] %> - t.timestamps -<% end -%> - end -<% attributes.select(&:token?).each do |attribute| -%> - add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true -<% end -%> -<% attributes_with_index.each do |attribute| -%> - add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> -<% end -%> - end -end diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt new file mode 100644 index 0000000000..5f7201cfe1 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt @@ -0,0 +1,24 @@ +class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] + def change + create_table :<%= table_name %><%= primary_key_type %> do |t| +<% attributes.each do |attribute| -%> +<% if attribute.password_digest? -%> + t.string :password_digest<%= attribute.inject_options %> +<% elsif attribute.token? -%> + t.string :<%= attribute.name %><%= attribute.inject_options %> +<% else -%> + t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %> +<% end -%> +<% end -%> +<% if options[:timestamps] %> + t.timestamps +<% end -%> + end +<% attributes.select(&:token?).each do |attribute| -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true +<% end -%> +<% attributes_with_index.each do |attribute| -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> +<% end -%> + end +end diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb deleted file mode 100644 index 481c70201b..0000000000 --- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb +++ /dev/null @@ -1,46 +0,0 @@ -class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] -<%- if migration_action == 'add' -%> - def change -<% attributes.each do |attribute| -%> - <%- if attribute.reference? -%> - add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> - <%- elsif attribute.token? -%> - add_column :<%= table_name %>, :<%= attribute.name %>, :string<%= attribute.inject_options %> - add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true - <%- else -%> - add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> - <%- if attribute.has_index? -%> - add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> - <%- end -%> - <%- end -%> -<%- end -%> - end -<%- elsif migration_action == 'join' -%> - def change - create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t| - <%- attributes.each do |attribute| -%> - <%- if attribute.reference? -%> - t.references :<%= attribute.name %><%= attribute.inject_options %> - <%- else -%> - <%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %> - <%- end -%> - <%- end -%> - end - end -<%- else -%> - def change -<% attributes.each do |attribute| -%> -<%- if migration_action -%> - <%- if attribute.reference? -%> - remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> - <%- else -%> - <%- if attribute.has_index? -%> - remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> - <%- end -%> - remove_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> - <%- end -%> -<%- end -%> -<%- end -%> - end -<%- end -%> -end diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt new file mode 100644 index 0000000000..481c70201b --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt @@ -0,0 +1,46 @@ +class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] +<%- if migration_action == 'add' -%> + def change +<% attributes.each do |attribute| -%> + <%- if attribute.reference? -%> + add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> + <%- elsif attribute.token? -%> + add_column :<%= table_name %>, :<%= attribute.name %>, :string<%= attribute.inject_options %> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true + <%- else -%> + add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> + <%- if attribute.has_index? -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + <%- end -%> +<%- end -%> + end +<%- elsif migration_action == 'join' -%> + def change + create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t| + <%- attributes.each do |attribute| -%> + <%- if attribute.reference? -%> + t.references :<%= attribute.name %><%= attribute.inject_options %> + <%- else -%> + <%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + <%- end -%> + end + end +<%- else -%> + def change +<% attributes.each do |attribute| -%> +<%- if migration_action -%> + <%- if attribute.reference? -%> + remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> + <%- else -%> + <%- if attribute.has_index? -%> + remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + remove_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> + <%- end -%> +<%- end -%> +<%- end -%> + end +<%- end -%> +end diff --git a/activerecord/lib/rails/generators/active_record/model/templates/model.rb b/activerecord/lib/rails/generators/active_record/model/templates/model.rb deleted file mode 100644 index 55dc65c8ad..0000000000 --- a/activerecord/lib/rails/generators/active_record/model/templates/model.rb +++ /dev/null @@ -1,13 +0,0 @@ -<% module_namespacing do -%> -class <%= class_name %> < <%= parent_class_name.classify %> -<% attributes.select(&:reference?).each do |attribute| -%> - belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %><%= ', required: true' if attribute.required? %> -<% end -%> -<% attributes.select(&:token?).each do |attribute| -%> - has_secure_token<% if attribute.name != "token" %> :<%= attribute.name %><% end %> -<% end -%> -<% if attributes.any?(&:password_digest?) -%> - has_secure_password -<% end -%> -end -<% end -%> diff --git a/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt b/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt new file mode 100644 index 0000000000..55dc65c8ad --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt @@ -0,0 +1,13 @@ +<% module_namespacing do -%> +class <%= class_name %> < <%= parent_class_name.classify %> +<% attributes.select(&:reference?).each do |attribute| -%> + belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %><%= ', required: true' if attribute.required? %> +<% end -%> +<% attributes.select(&:token?).each do |attribute| -%> + has_secure_token<% if attribute.name != "token" %> :<%= attribute.name %><% end %> +<% end -%> +<% if attributes.any?(&:password_digest?) -%> + has_secure_password +<% end -%> +end +<% end -%> diff --git a/activerecord/lib/rails/generators/active_record/model/templates/module.rb b/activerecord/lib/rails/generators/active_record/model/templates/module.rb deleted file mode 100644 index a3bf1c37b6..0000000000 --- a/activerecord/lib/rails/generators/active_record/model/templates/module.rb +++ /dev/null @@ -1,7 +0,0 @@ -<% module_namespacing do -%> -module <%= class_path.map(&:camelize).join('::') %> - def self.table_name_prefix - '<%= namespaced? ? namespaced_class_path.join('_') : class_path.join('_') %>_' - end -end -<% end -%> diff --git a/activerecord/lib/rails/generators/active_record/model/templates/module.rb.tt b/activerecord/lib/rails/generators/active_record/model/templates/module.rb.tt new file mode 100644 index 0000000000..a3bf1c37b6 --- /dev/null +++ b/activerecord/lib/rails/generators/active_record/model/templates/module.rb.tt @@ -0,0 +1,7 @@ +<% module_namespacing do -%> +module <%= class_path.map(&:camelize).join('::') %> + def self.table_name_prefix + '<%= namespaced? ? namespaced_class_path.join('_') : class_path.join('_') %>_' + end +end +<% end -%> diff --git a/railties/lib/rails/generators/erb/controller/templates/view.html.erb b/railties/lib/rails/generators/erb/controller/templates/view.html.erb deleted file mode 100644 index cd54d13d83..0000000000 --- a/railties/lib/rails/generators/erb/controller/templates/view.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -

<%= class_name %>#<%= @action %>

-

Find me in <%= @path %>

diff --git a/railties/lib/rails/generators/erb/controller/templates/view.html.erb.tt b/railties/lib/rails/generators/erb/controller/templates/view.html.erb.tt new file mode 100644 index 0000000000..cd54d13d83 --- /dev/null +++ b/railties/lib/rails/generators/erb/controller/templates/view.html.erb.tt @@ -0,0 +1,2 @@ +

<%= class_name %>#<%= @action %>

+

Find me in <%= @path %>

diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.html.erb b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb deleted file mode 100644 index b5045671b3..0000000000 --- a/railties/lib/rails/generators/erb/mailer/templates/view.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

<%= class_name %>#<%= @action %>

- -

- <%%= @greeting %>, find me in <%= @path %> -

diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.html.erb.tt b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb.tt new file mode 100644 index 0000000000..b5045671b3 --- /dev/null +++ b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb.tt @@ -0,0 +1,5 @@ +

<%= class_name %>#<%= @action %>

+ +

+ <%%= @greeting %>, find me in <%= @path %> +

diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.text.erb b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb deleted file mode 100644 index 342285df19..0000000000 --- a/railties/lib/rails/generators/erb/mailer/templates/view.text.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= class_name %>#<%= @action %> - -<%%= @greeting %>, find me in <%= @path %> diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.text.erb.tt b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb.tt new file mode 100644 index 0000000000..342285df19 --- /dev/null +++ b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb.tt @@ -0,0 +1,3 @@ +<%= class_name %>#<%= @action %> + +<%%= @greeting %>, find me in <%= @path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb deleted file mode 100644 index 0eb9d82bbb..0000000000 --- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb +++ /dev/null @@ -1,34 +0,0 @@ -<%%= form_with(model: <%= model_resource_name %>, local: true) do |form| %> - <%% if <%= singular_table_name %>.errors.any? %> -
-

<%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:

- -
    - <%% <%= singular_table_name %>.errors.full_messages.each do |message| %> -
  • <%%= message %>
  • - <%% end %> -
-
- <%% end %> - -<% attributes.each do |attribute| -%> -
-<% if attribute.password_digest? -%> - <%%= form.label :password %> - <%%= form.password_field :password, id: :<%= field_id(:password) %> %> -
- -
- <%%= form.label :password_confirmation %> - <%%= form.password_field :password_confirmation, id: :<%= field_id(:password_confirmation) %> %> -<% else -%> - <%%= form.label :<%= attribute.column_name %> %> - <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, id: :<%= field_id(attribute.column_name) %> %> -<% end -%> -
- -<% end -%> -
- <%%= form.submit %> -
-<%% end %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt new file mode 100644 index 0000000000..0eb9d82bbb --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt @@ -0,0 +1,34 @@ +<%%= form_with(model: <%= model_resource_name %>, local: true) do |form| %> + <%% if <%= singular_table_name %>.errors.any? %> +
+

<%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:

+ +
    + <%% <%= singular_table_name %>.errors.full_messages.each do |message| %> +
  • <%%= message %>
  • + <%% end %> +
+
+ <%% end %> + +<% attributes.each do |attribute| -%> +
+<% if attribute.password_digest? -%> + <%%= form.label :password %> + <%%= form.password_field :password, id: :<%= field_id(:password) %> %> +
+ +
+ <%%= form.label :password_confirmation %> + <%%= form.password_field :password_confirmation, id: :<%= field_id(:password_confirmation) %> %> +<% else -%> + <%%= form.label :<%= attribute.column_name %> %> + <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, id: :<%= field_id(attribute.column_name) %> %> +<% end -%> +
+ +<% end -%> +
+ <%%= form.submit %> +
+<%% end %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb deleted file mode 100644 index 81329473d9..0000000000 --- a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

Editing <%= singular_table_name.titleize %>

- -<%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %> - -<%%= link_to 'Show', @<%= singular_table_name %> %> | -<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt new file mode 100644 index 0000000000..81329473d9 --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt @@ -0,0 +1,6 @@ +

Editing <%= singular_table_name.titleize %>

+ +<%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %> + +<%%= link_to 'Show', @<%= singular_table_name %> %> | +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb deleted file mode 100644 index e1ede7c713..0000000000 --- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb +++ /dev/null @@ -1,31 +0,0 @@ -

<%%= notice %>

- -

<%= plural_table_name.titleize %>

- -
- - -<% attributes.reject(&:password_digest?).each do |attribute| -%> - -<% end -%> - - - - - - <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> - -<% attributes.reject(&:password_digest?).each do |attribute| -%> - -<% end -%> - - - - - <%% end %> - -
<%= attribute.human_name %>
<%%= <%= singular_table_name %>.<%= attribute.name %> %><%%= link_to 'Show', <%= model_resource_name %> %><%%= link_to 'Edit', edit_<%= singular_route_name %>_path(<%= singular_table_name %>) %><%%= link_to 'Destroy', <%= model_resource_name %>, method: :delete, data: { confirm: 'Are you sure?' } %>
- -
- -<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_route_name %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt new file mode 100644 index 0000000000..e1ede7c713 --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt @@ -0,0 +1,31 @@ +

<%%= notice %>

+ +

<%= plural_table_name.titleize %>

+ + + + +<% attributes.reject(&:password_digest?).each do |attribute| -%> + +<% end -%> + + + + + + <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> + +<% attributes.reject(&:password_digest?).each do |attribute| -%> + +<% end -%> + + + + + <%% end %> + +
<%= attribute.human_name %>
<%%= <%= singular_table_name %>.<%= attribute.name %> %><%%= link_to 'Show', <%= model_resource_name %> %><%%= link_to 'Edit', edit_<%= singular_route_name %>_path(<%= singular_table_name %>) %><%%= link_to 'Destroy', <%= model_resource_name %>, method: :delete, data: { confirm: 'Are you sure?' } %>
+ +
+ +<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_route_name %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb deleted file mode 100644 index 9b2b2f4875..0000000000 --- a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

New <%= singular_table_name.titleize %>

- -<%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %> - -<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt new file mode 100644 index 0000000000..9b2b2f4875 --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt @@ -0,0 +1,5 @@ +

New <%= singular_table_name.titleize %>

+ +<%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %> + +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb deleted file mode 100644 index 5e634153be..0000000000 --- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -

<%%= notice %>

- -<% attributes.reject(&:password_digest?).each do |attribute| -%> -

- <%= attribute.human_name %>: - <%%= @<%= singular_table_name %>.<%= attribute.name %> %> -

- -<% end -%> -<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(@<%= singular_table_name %>) %> | -<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt new file mode 100644 index 0000000000..5e634153be --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt @@ -0,0 +1,11 @@ +

<%%= notice %>

+ +<% attributes.reject(&:password_digest?).each do |attribute| -%> +

+ <%= attribute.human_name %>: + <%%= @<%= singular_table_name %>.<%= attribute.name %> %> +

+ +<% end -%> +<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(@<%= singular_table_name %>) %> | +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile deleted file mode 100644 index 61026f5182..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ /dev/null @@ -1,76 +0,0 @@ -source 'https://rubygems.org' -git_source(:github) { |repo| "https://github.com/#{repo}.git" } - -ruby <%= "'#{RUBY_VERSION}'" -%> - -<% unless gemfile_entries.first.comment -%> - -<% end -%> -<% gemfile_entries.each do |gem| -%> -<% if gem.comment -%> - -# <%= gem.comment %> -<% end -%> -<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%> -<% if gem.options.any? -%> -, <%= gem.options.map { |k,v| - "#{k}: #{v.inspect}" }.join(', ') %> -<% end -%> -<% end -%> - -# Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' -<% unless skip_active_storage? -%> - -# Use ActiveStorage variant -# gem 'mini_magick', '~> 4.8' -<% end -%> - -# Use Capistrano for deployment -# gem 'capistrano-rails', group: :development - -# Reduces boot times through caching; required in config/boot.rb -gem 'bootsnap', '>= 1.1.0', require: false - -<%- if options.api? -%> -# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible -# gem 'rack-cors' - -<%- end -%> -<% if RUBY_ENGINE == 'ruby' -%> -group :development, :test do - # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] - <%- if depends_on_system_test? -%> - # Adds support for Capybara system testing and selenium driver - gem 'capybara', '~> 2.15' - gem 'selenium-webdriver' - # Easy installation and use of chromedriver to run system tests with Chrome - gem 'chromedriver-helper' - <%- end -%> -end - -group :development do -<%- unless options.api? -%> - # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - <%- if options.dev? || options.edge? -%> - gem 'web-console', github: 'rails/web-console' - <%- else -%> - gem 'web-console', '>= 3.3.0' - <%- end -%> -<%- end -%> -<% if depend_on_listen? -%> - gem 'listen', '>= 3.0.5', '< 3.2' -<% end -%> -<% if spring_install? -%> - # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring - gem 'spring' -<% if depend_on_listen? -%> - gem 'spring-watcher-listen', '~> 2.0.0' -<% end -%> -<% end -%> -end -<% end -%> - -# Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt new file mode 100644 index 0000000000..61026f5182 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt @@ -0,0 +1,76 @@ +source 'https://rubygems.org' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +ruby <%= "'#{RUBY_VERSION}'" -%> + +<% unless gemfile_entries.first.comment -%> + +<% end -%> +<% gemfile_entries.each do |gem| -%> +<% if gem.comment -%> + +# <%= gem.comment %> +<% end -%> +<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%> +<% if gem.options.any? -%> +, <%= gem.options.map { |k,v| + "#{k}: #{v.inspect}" }.join(', ') %> +<% end -%> +<% end -%> + +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' +<% unless skip_active_storage? -%> + +# Use ActiveStorage variant +# gem 'mini_magick', '~> 4.8' +<% end -%> + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +# Reduces boot times through caching; required in config/boot.rb +gem 'bootsnap', '>= 1.1.0', require: false + +<%- if options.api? -%> +# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible +# gem 'rack-cors' + +<%- end -%> +<% if RUBY_ENGINE == 'ruby' -%> +group :development, :test do + # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] + <%- if depends_on_system_test? -%> + # Adds support for Capybara system testing and selenium driver + gem 'capybara', '~> 2.15' + gem 'selenium-webdriver' + # Easy installation and use of chromedriver to run system tests with Chrome + gem 'chromedriver-helper' + <%- end -%> +end + +group :development do +<%- unless options.api? -%> + # Access an interactive console on exception pages or by calling 'console' anywhere in the code. + <%- if options.dev? || options.edge? -%> + gem 'web-console', github: 'rails/web-console' + <%- else -%> + gem 'web-console', '>= 3.3.0' + <%- end -%> +<%- end -%> +<% if depend_on_listen? -%> + gem 'listen', '>= 3.0.5', '< 3.2' +<% end -%> +<% if spring_install? -%> + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' +<% if depend_on_listen? -%> + gem 'spring-watcher-listen', '~> 2.0.0' +<% end -%> +<% end -%> +end +<% end -%> + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/railties/lib/rails/generators/rails/app/templates/README.md b/railties/lib/rails/generators/rails/app/templates/README.md deleted file mode 100644 index 7db80e4ca1..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# README - -This README would normally document whatever steps are necessary to get the -application up and running. - -Things you may want to cover: - -* Ruby version - -* System dependencies - -* Configuration - -* Database creation - -* Database initialization - -* How to run the test suite - -* Services (job queues, cache servers, search engines, etc.) - -* Deployment instructions - -* ... diff --git a/railties/lib/rails/generators/rails/app/templates/README.md.tt b/railties/lib/rails/generators/rails/app/templates/README.md.tt new file mode 100644 index 0000000000..7db80e4ca1 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/README.md.tt @@ -0,0 +1,24 @@ +# README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile deleted file mode 100644 index e85f913914..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/Rakefile +++ /dev/null @@ -1,6 +0,0 @@ -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. - -require_relative 'config/application' - -Rails.application.load_tasks diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile.tt b/railties/lib/rails/generators/rails/app/templates/Rakefile.tt new file mode 100644 index 0000000000..e85f913914 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/Rakefile.tt @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js deleted file mode 100644 index 739aa5f022..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js +++ /dev/null @@ -1,13 +0,0 @@ -// Action Cable provides the framework to deal with WebSockets in Rails. -// You can generate new channels where WebSocket features live using the `rails generate channel` command. -// -//= require action_cable -//= require_self -//= require_tree ./channels - -(function() { - this.App || (this.App = {}); - - App.cable = ActionCable.createConsumer(); - -}).call(this); diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt new file mode 100644 index 0000000000..739aa5f022 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt @@ -0,0 +1,13 @@ +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the `rails generate channel` command. +// +//= require action_cable +//= require_self +//= require_tree ./channels + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css deleted file mode 100644 index d05ea0f511..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css +++ /dev/null @@ -1,15 +0,0 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's - * vendor/assets/stylesheets directory can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the bottom of the - * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS - * files in this directory. Styles in this file should be added after the last require_* statement. - * It is generally better to create a new file per style scope. - * - *= require_tree . - *= require_self - */ diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt new file mode 100644 index 0000000000..d05ea0f511 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's + * vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb deleted file mode 100644 index d672697283..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Channel < ActionCable::Channel::Base - end -end diff --git a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb.tt new file mode 100644 index 0000000000..d672697283 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb.tt @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb deleted file mode 100644 index 0ff5442f47..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Connection < ActionCable::Connection::Base - end -end diff --git a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb.tt new file mode 100644 index 0000000000..0ff5442f47 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb.tt @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb deleted file mode 100644 index de6be7945c..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ApplicationHelper -end diff --git a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb.tt new file mode 100644 index 0000000000..de6be7945c --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb.tt @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb deleted file mode 100644 index a009ace51c..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb +++ /dev/null @@ -1,2 +0,0 @@ -class ApplicationJob < ActiveJob::Base -end diff --git a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt new file mode 100644 index 0000000000..a009ace51c --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb b/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb deleted file mode 100644 index 286b2239d1..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb +++ /dev/null @@ -1,4 +0,0 @@ -class ApplicationMailer < ActionMailer::Base - default from: 'from@example.com' - layout 'mailer' -end diff --git a/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt new file mode 100644 index 0000000000..286b2239d1 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb deleted file mode 100644 index 10a4cba84d..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb +++ /dev/null @@ -1,3 +0,0 @@ -class ApplicationRecord < ActiveRecord::Base - self.abstract_class = true -end diff --git a/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt new file mode 100644 index 0000000000..10a4cba84d --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle b/railties/lib/rails/generators/rails/app/templates/bin/bundle deleted file mode 100644 index a84f0afe47..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/bin/bundle +++ /dev/null @@ -1,2 +0,0 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -load Gem.bin_path('bundler', 'bundle') diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt b/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt new file mode 100644 index 0000000000..a84f0afe47 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt @@ -0,0 +1,2 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +load Gem.bin_path('bundler', 'bundle') diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rails b/railties/lib/rails/generators/rails/app/templates/bin/rails deleted file mode 100644 index 513a2e0183..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/bin/rails +++ /dev/null @@ -1,3 +0,0 @@ -APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rails.tt b/railties/lib/rails/generators/rails/app/templates/bin/rails.tt new file mode 100644 index 0000000000..513a2e0183 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/rails.tt @@ -0,0 +1,3 @@ +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rake b/railties/lib/rails/generators/rails/app/templates/bin/rake deleted file mode 100644 index d14fc8395b..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/bin/rake +++ /dev/null @@ -1,3 +0,0 @@ -require_relative '../config/boot' -require 'rake' -Rake.application.run diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rake.tt b/railties/lib/rails/generators/rails/app/templates/bin/rake.tt new file mode 100644 index 0000000000..d14fc8395b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/rake.tt @@ -0,0 +1,3 @@ +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn b/railties/lib/rails/generators/rails/app/templates/bin/yarn deleted file mode 100644 index b4e4d95286..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/bin/yarn +++ /dev/null @@ -1,10 +0,0 @@ -APP_ROOT = File.expand_path('..', __dir__) -Dir.chdir(APP_ROOT) do - begin - exec "yarnpkg #{ARGV.join(' ')}" - rescue Errno::ENOENT - $stderr.puts "Yarn executable was not detected in the system." - $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" - exit 1 - end -end diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn.tt b/railties/lib/rails/generators/rails/app/templates/bin/yarn.tt new file mode 100644 index 0000000000..b4e4d95286 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/yarn.tt @@ -0,0 +1,10 @@ +APP_ROOT = File.expand_path('..', __dir__) +Dir.chdir(APP_ROOT) do + begin + exec "yarnpkg #{ARGV.join(' ')}" + rescue Errno::ENOENT + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 + end +end diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru deleted file mode 100644 index f7ba0b527b..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config.ru +++ /dev/null @@ -1,5 +0,0 @@ -# This file is used by Rack-based servers to start the application. - -require_relative 'config/environment' - -run Rails.application diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru.tt b/railties/lib/rails/generators/rails/app/templates/config.ru.tt new file mode 100644 index 0000000000..f7ba0b527b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config.ru.tt @@ -0,0 +1,5 @@ +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb deleted file mode 100644 index 9e03e86771..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ /dev/null @@ -1,44 +0,0 @@ -require_relative 'boot' - -<% if include_all_railties? -%> -require 'rails/all' -<% else -%> -require "rails" -# Pick the frameworks you want: -require "active_model/railtie" -require "active_job/railtie" -<%= comment_if :skip_active_record %>require "active_record/railtie" -<%= comment_if :skip_active_storage %>require "active_storage/engine" -require "action_controller/railtie" -<%= comment_if :skip_action_mailer %>require "action_mailer/railtie" -require "action_view/railtie" -<%= comment_if :skip_action_cable %>require "action_cable/engine" -<%= comment_if :skip_sprockets %>require "sprockets/railtie" -<%= comment_if :skip_test %>require "rails/test_unit/railtie" -<% end -%> - -# Require the gems listed in Gemfile, including any gems -# you've limited to :test, :development, or :production. -Bundler.require(*Rails.groups) - -module <%= app_const_base %> - class Application < Rails::Application - # Initialize configuration defaults for originally generated Rails version. - config.load_defaults <%= Rails::VERSION::STRING.to_f %> - - # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. -<%- if options.api? -%> - - # Only loads a smaller set of middleware suitable for API only apps. - # Middleware like session, flash, cookies can be added back manually. - # Skip views, helpers and assets when generating a new resource. - config.api_only = true -<%- elsif !depends_on_system_test? -%> - - # Don't generate system test files. - config.generators.system_tests = nil -<%- end -%> - end -end diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt new file mode 100644 index 0000000000..9e03e86771 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt @@ -0,0 +1,44 @@ +require_relative 'boot' + +<% if include_all_railties? -%> +require 'rails/all' +<% else -%> +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +<%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" +require "action_controller/railtie" +<%= comment_if :skip_action_mailer %>require "action_mailer/railtie" +require "action_view/railtie" +<%= comment_if :skip_action_cable %>require "action_cable/engine" +<%= comment_if :skip_sprockets %>require "sprockets/railtie" +<%= comment_if :skip_test %>require "rails/test_unit/railtie" +<% end -%> + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module <%= app_const_base %> + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults <%= Rails::VERSION::STRING.to_f %> + + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. +<%- if options.api? -%> + + # Only loads a smaller set of middleware suitable for API only apps. + # Middleware like session, flash, cookies can be added back manually. + # Skip views, helpers and assets when generating a new resource. + config.api_only = true +<%- elsif !depends_on_system_test? -%> + + # Don't generate system test files. + config.generators.system_tests = nil +<%- end -%> + end +end diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb deleted file mode 100644 index b9e460cef3..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/boot.rb +++ /dev/null @@ -1,4 +0,0 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -require 'bundler/setup' # Set up gems listed in the Gemfile. -require 'bootsnap/setup' # Speed up boot time by caching expensive operations. diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/boot.rb.tt new file mode 100644 index 0000000000..b9e460cef3 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb.tt @@ -0,0 +1,4 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. +require 'bootsnap/setup' # Speed up boot time by caching expensive operations. diff --git a/railties/lib/rails/generators/rails/app/templates/config/cable.yml b/railties/lib/rails/generators/rails/app/templates/config/cable.yml deleted file mode 100644 index 8e53156c71..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/cable.yml +++ /dev/null @@ -1,10 +0,0 @@ -development: - adapter: async - -test: - adapter: async - -production: - adapter: redis - url: <%%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> - channel_prefix: <%= app_name %>_production diff --git a/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt new file mode 100644 index 0000000000..8e53156c71 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/cable.yml.tt @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: <%%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: <%= app_name %>_production diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml deleted file mode 100644 index 917b52e535..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml +++ /dev/null @@ -1,50 +0,0 @@ -# FrontBase versions 4.x -# -# Get the bindings: -# gem install ruby-frontbase -# -# Configure Using Gemfile -# gem 'ruby-frontbase' -# -default: &default - adapter: frontbase - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - host: localhost - username: <%= app_name %> - password: '' - -development: - <<: *default - database: <%= app_name %>_development - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: <%= app_name %>_test - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="frontbase://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%%= ENV['DATABASE_URL'] %> -# -production: - <<: *default - database: <%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt new file mode 100644 index 0000000000..917b52e535 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt @@ -0,0 +1,50 @@ +# FrontBase versions 4.x +# +# Get the bindings: +# gem install ruby-frontbase +# +# Configure Using Gemfile +# gem 'ruby-frontbase' +# +default: &default + adapter: frontbase + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + host: localhost + username: <%= app_name %> + password: '' + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="frontbase://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml deleted file mode 100644 index d40117a27f..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml +++ /dev/null @@ -1,86 +0,0 @@ -# IBM Dataservers -# -# Home Page -# https://github.com/dparnell/ibm_db -# -# To install the ibm_db gem: -# -# On Linux: -# . /home/db2inst1/sqllib/db2profile -# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include -# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib32 -# gem install ibm_db -# -# On Mac OS X 10.5: -# . /home/db2inst1/sqllib/db2profile -# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include -# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib32 -# export ARCHFLAGS="-arch i386" -# gem install ibm_db -# -# On Mac OS X 10.6: -# . /home/db2inst1/sqllib/db2profile -# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include -# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib64 -# export ARCHFLAGS="-arch x86_64" -# gem install ibm_db -# -# On Windows: -# Issue the command: gem install ibm_db -# -# Configure Using Gemfile -# gem 'ibm_db' -# -# -default: &default - adapter: ibm_db - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - username: db2inst1 - password: - #schema: db2inst1 - #host: localhost - #port: 50000 - #account: my_account - #app_user: my_app_user - #application: my_application - #workstation: my_workstation - #security: SSL - #timeout: 10 - #authentication: SERVER - #parameterized: false - -development: - <<: *default - database: <%= app_name[0,4] %>_dev - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: <%= app_name[0,4] %>_tst - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="ibm-db://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%%= ENV['DATABASE_URL'] %> -# -production: - <<: *default - database: <%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt new file mode 100644 index 0000000000..d40117a27f --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt @@ -0,0 +1,86 @@ +# IBM Dataservers +# +# Home Page +# https://github.com/dparnell/ibm_db +# +# To install the ibm_db gem: +# +# On Linux: +# . /home/db2inst1/sqllib/db2profile +# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include +# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib32 +# gem install ibm_db +# +# On Mac OS X 10.5: +# . /home/db2inst1/sqllib/db2profile +# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include +# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib32 +# export ARCHFLAGS="-arch i386" +# gem install ibm_db +# +# On Mac OS X 10.6: +# . /home/db2inst1/sqllib/db2profile +# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include +# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib64 +# export ARCHFLAGS="-arch x86_64" +# gem install ibm_db +# +# On Windows: +# Issue the command: gem install ibm_db +# +# Configure Using Gemfile +# gem 'ibm_db' +# +# +default: &default + adapter: ibm_db + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + username: db2inst1 + password: + #schema: db2inst1 + #host: localhost + #port: 50000 + #account: my_account + #app_user: my_app_user + #application: my_application + #workstation: my_workstation + #security: SSL + #timeout: 10 + #authentication: SERVER + #parameterized: false + +development: + <<: *default + database: <%= app_name[0,4] %>_dev + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name[0,4] %>_tst + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="ibm-db://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml deleted file mode 100644 index 563be77710..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml +++ /dev/null @@ -1,69 +0,0 @@ -# If you are using mssql, derby, hsqldb, or h2 with one of the -# ActiveRecord JDBC adapters, install the appropriate driver, e.g.,: -# gem install activerecord-jdbcmssql-adapter -# -# Configure using Gemfile: -# gem 'activerecord-jdbcmssql-adapter' -# -# development: -# adapter: mssql -# username: <%= app_name %> -# password: -# host: localhost -# database: <%= app_name %>_development -# -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -# -# test: -# adapter: mssql -# username: <%= app_name %> -# password: -# host: localhost -# database: <%= app_name %>_test -# -# production: -# adapter: mssql -# username: <%= app_name %> -# password: -# host: localhost -# database: <%= app_name %>_production - -# If you are using oracle, db2, sybase, informix or prefer to use the plain -# JDBC adapter, configure your database setting as the example below (requires -# you to download and manually install the database vendor's JDBC driver .jar -# file). See your driver documentation for the appropriate driver class and -# connection string: - -default: &default - adapter: jdbc - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - username: <%= app_name %> - password: - driver: - -development: - <<: *default - url: jdbc:db://localhost/<%= app_name %>_development - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - url: jdbc:db://localhost/<%= app_name %>_test - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -production: - url: jdbc:db://localhost/<%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt new file mode 100644 index 0000000000..563be77710 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt @@ -0,0 +1,69 @@ +# If you are using mssql, derby, hsqldb, or h2 with one of the +# ActiveRecord JDBC adapters, install the appropriate driver, e.g.,: +# gem install activerecord-jdbcmssql-adapter +# +# Configure using Gemfile: +# gem 'activerecord-jdbcmssql-adapter' +# +# development: +# adapter: mssql +# username: <%= app_name %> +# password: +# host: localhost +# database: <%= app_name %>_development +# +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +# +# test: +# adapter: mssql +# username: <%= app_name %> +# password: +# host: localhost +# database: <%= app_name %>_test +# +# production: +# adapter: mssql +# username: <%= app_name %> +# password: +# host: localhost +# database: <%= app_name %>_production + +# If you are using oracle, db2, sybase, informix or prefer to use the plain +# JDBC adapter, configure your database setting as the example below (requires +# you to download and manually install the database vendor's JDBC driver .jar +# file). See your driver documentation for the appropriate driver class and +# connection string: + +default: &default + adapter: jdbc + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + username: <%= app_name %> + password: + driver: + +development: + <<: *default + url: jdbc:db://localhost/<%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + url: jdbc:db://localhost/<%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +production: + url: jdbc:db://localhost/<%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> 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 deleted file mode 100644 index 2a67bdca25..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml +++ /dev/null @@ -1,53 +0,0 @@ -# MySQL. Versions 5.1.10 and up are supported. -# -# Install the MySQL driver: -# gem install activerecord-jdbcmysql-adapter -# -# Configure Using Gemfile -# gem 'activerecord-jdbcmysql-adapter' -# -# And be sure to use new-style password hashing: -# https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html -# -default: &default - adapter: mysql - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - username: root - password: - host: localhost - -development: - <<: *default - database: <%= app_name %>_development - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: <%= app_name %>_test - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="mysql://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%%= ENV['DATABASE_URL'] %> -# -production: - <<: *default - database: <%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt new file mode 100644 index 0000000000..2a67bdca25 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt @@ -0,0 +1,53 @@ +# MySQL. Versions 5.1.10 and up are supported. +# +# Install the MySQL driver: +# gem install activerecord-jdbcmysql-adapter +# +# Configure Using Gemfile +# gem 'activerecord-jdbcmysql-adapter' +# +# And be sure to use new-style password hashing: +# https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html +# +default: &default + adapter: mysql + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + username: root + password: + host: localhost + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="mysql://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml deleted file mode 100644 index 70df04079d..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml +++ /dev/null @@ -1,69 +0,0 @@ -# PostgreSQL. Versions 9.1 and up are supported. -# -# Configure Using Gemfile -# gem 'activerecord-jdbcpostgresql-adapter' -# -default: &default - adapter: postgresql - encoding: unicode - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - -development: - <<: *default - database: <%= app_name %>_development - - # The specified database role being used to connect to postgres. - # To create additional roles in postgres see `$ createuser --help`. - # When left blank, postgres will use the default role. This is - # the same name as the operating system user that initialized the database. - #username: <%= app_name %> - - # The password associated with the postgres role (username). - #password: - - # Connect on a TCP socket. Omitted by default since the client uses a - # domain socket that doesn't need configuration. Windows does not have - # domain sockets, so uncomment these lines. - #host: localhost - #port: 5432 - - # Schema search path. The server defaults to $user,public - #schema_search_path: myapp,sharedapp,public - - # Minimum log levels, in increasing order: - # debug5, debug4, debug3, debug2, debug1, - # log, notice, warning, error, fatal, and panic - # Defaults to warning. - #min_messages: notice - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: <%= app_name %>_test - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%%= ENV['DATABASE_URL'] %> -# -production: - <<: *default - database: <%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt new file mode 100644 index 0000000000..70df04079d --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt @@ -0,0 +1,69 @@ +# PostgreSQL. Versions 9.1 and up are supported. +# +# Configure Using Gemfile +# gem 'activerecord-jdbcpostgresql-adapter' +# +default: &default + adapter: postgresql + encoding: unicode + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + +development: + <<: *default + database: <%= app_name %>_development + + # The specified database role being used to connect to postgres. + # To create additional roles in postgres see `$ createuser --help`. + # When left blank, postgres will use the default role. This is + # the same name as the operating system user that initialized the database. + #username: <%= app_name %> + + # The password associated with the postgres role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml deleted file mode 100644 index 371415e6a8..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml +++ /dev/null @@ -1,24 +0,0 @@ -# SQLite version 3.x -# gem 'activerecord-jdbcsqlite3-adapter' -# -# Configure Using Gemfile -# gem 'activerecord-jdbcsqlite3-adapter' -# -default: &default - adapter: sqlite3 - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - -development: - <<: *default - database: db/development.sqlite3 - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: db/test.sqlite3 - -production: - <<: *default - database: db/production.sqlite3 diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt new file mode 100644 index 0000000000..371415e6a8 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt @@ -0,0 +1,24 @@ +# SQLite version 3.x +# gem 'activerecord-jdbcsqlite3-adapter' +# +# Configure Using Gemfile +# gem 'activerecord-jdbcsqlite3-adapter' +# +default: &default + adapter: sqlite3 + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 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 deleted file mode 100644 index 04afaa0596..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml +++ /dev/null @@ -1,58 +0,0 @@ -# MySQL. Versions 5.1.10 and up are supported. -# -# Install the MySQL driver -# gem install mysql2 -# -# Ensure the MySQL gem is defined in your Gemfile -# gem 'mysql2' -# -# And be sure to use new-style password hashing: -# https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html -# -default: &default - adapter: mysql2 - encoding: utf8 - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - username: root - password: -<% if mysql_socket -%> - socket: <%= mysql_socket %> -<% else -%> - host: localhost -<% end -%> - -development: - <<: *default - database: <%= app_name %>_development - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: <%= app_name %>_test - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%%= ENV['DATABASE_URL'] %> -# -production: - <<: *default - database: <%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt new file mode 100644 index 0000000000..04afaa0596 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt @@ -0,0 +1,58 @@ +# MySQL. Versions 5.1.10 and up are supported. +# +# Install the MySQL driver +# gem install mysql2 +# +# Ensure the MySQL gem is defined in your Gemfile +# gem 'mysql2' +# +# And be sure to use new-style password hashing: +# https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html +# +default: &default + adapter: mysql2 + encoding: utf8 + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + username: root + password: +<% if mysql_socket -%> + socket: <%= mysql_socket %> +<% else -%> + host: localhost +<% end -%> + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml deleted file mode 100644 index 6da0601b24..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml +++ /dev/null @@ -1,61 +0,0 @@ -# Oracle/OCI 11g or higher recommended -# -# Requires Ruby/OCI8: -# https://github.com/kubo/ruby-oci8 -# -# Specify your database using any valid connection syntax, such as a -# tnsnames.ora service name, or an SQL connect string of the form: -# -# //host:[port][/service name] -# -# By default prefetch_rows (OCI_ATTR_PREFETCH_ROWS) is set to 100. And -# until true bind variables are supported, cursor_sharing is set by default -# to 'similar'. Both can be changed in the configuration below; the defaults -# are equivalent to specifying: -# -# prefetch_rows: 100 -# cursor_sharing: similar -# -default: &default - adapter: oracle_enhanced - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - username: <%= app_name %> - password: - -development: - <<: *default - database: <%= app_name %>_development - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: <%= app_name %>_test - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="oracle-enhanced://myuser:mypass@localhost/somedatabase" -# -# Note that the adapter name uses a dash instead of an underscore. -# -# You can use this database configuration with: -# -# production: -# url: <%%= ENV['DATABASE_URL'] %> -# -production: - <<: *default - database: <%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt new file mode 100644 index 0000000000..6da0601b24 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt @@ -0,0 +1,61 @@ +# Oracle/OCI 11g or higher recommended +# +# Requires Ruby/OCI8: +# https://github.com/kubo/ruby-oci8 +# +# Specify your database using any valid connection syntax, such as a +# tnsnames.ora service name, or an SQL connect string of the form: +# +# //host:[port][/service name] +# +# By default prefetch_rows (OCI_ATTR_PREFETCH_ROWS) is set to 100. And +# until true bind variables are supported, cursor_sharing is set by default +# to 'similar'. Both can be changed in the configuration below; the defaults +# are equivalent to specifying: +# +# prefetch_rows: 100 +# cursor_sharing: similar +# +default: &default + adapter: oracle_enhanced + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + username: <%= app_name %> + password: + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="oracle-enhanced://myuser:mypass@localhost/somedatabase" +# +# Note that the adapter name uses a dash instead of an underscore. +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml deleted file mode 100644 index 145cfb7f74..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml +++ /dev/null @@ -1,85 +0,0 @@ -# PostgreSQL. Versions 9.1 and up are supported. -# -# Install the pg driver: -# gem install pg -# On OS X with Homebrew: -# gem install pg -- --with-pg-config=/usr/local/bin/pg_config -# On OS X with MacPorts: -# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config -# On Windows: -# gem install pg -# Choose the win32 build. -# Install PostgreSQL and put its /bin directory on your path. -# -# Configure Using Gemfile -# gem 'pg' -# -default: &default - adapter: postgresql - encoding: unicode - # For details on connection pooling, see Rails configuration guide - # http://guides.rubyonrails.org/configuring.html#database-pooling - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - -development: - <<: *default - database: <%= app_name %>_development - - # The specified database role being used to connect to postgres. - # To create additional roles in postgres see `$ createuser --help`. - # When left blank, postgres will use the default role. This is - # the same name as the operating system user that initialized the database. - #username: <%= app_name %> - - # The password associated with the postgres role (username). - #password: - - # Connect on a TCP socket. Omitted by default since the client uses a - # domain socket that doesn't need configuration. Windows does not have - # domain sockets, so uncomment these lines. - #host: localhost - - # The TCP port the server listens on. Defaults to 5432. - # If your server runs on a different port number, change accordingly. - #port: 5432 - - # Schema search path. The server defaults to $user,public - #schema_search_path: myapp,sharedapp,public - - # Minimum log levels, in increasing order: - # debug5, debug4, debug3, debug2, debug1, - # log, notice, warning, error, fatal, and panic - # Defaults to warning. - #min_messages: notice - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: <%= app_name %>_test - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%%= ENV['DATABASE_URL'] %> -# -production: - <<: *default - database: <%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt new file mode 100644 index 0000000000..145cfb7f74 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt @@ -0,0 +1,85 @@ +# PostgreSQL. Versions 9.1 and up are supported. +# +# Install the pg driver: +# gem install pg +# On OS X with Homebrew: +# gem install pg -- --with-pg-config=/usr/local/bin/pg_config +# On OS X with MacPorts: +# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem 'pg' +# +default: &default + adapter: postgresql + encoding: unicode + # For details on connection pooling, see Rails configuration guide + # http://guides.rubyonrails.org/configuring.html#database-pooling + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + +development: + <<: *default + database: <%= app_name %>_development + + # The specified database role being used to connect to postgres. + # To create additional roles in postgres see `$ createuser --help`. + # When left blank, postgres will use the default role. This is + # the same name as the operating system user that initialized the database. + #username: <%= app_name %> + + # The password associated with the postgres role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + + # The TCP port the server listens on. Defaults to 5432. + # If your server runs on a different port number, change accordingly. + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml deleted file mode 100644 index 9510568124..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml +++ /dev/null @@ -1,25 +0,0 @@ -# SQLite version 3.x -# gem install sqlite3 -# -# Ensure the SQLite 3 gem is defined in your Gemfile -# gem 'sqlite3' -# -default: &default - adapter: sqlite3 - pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 - -development: - <<: *default - database: db/development.sqlite3 - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: db/test.sqlite3 - -production: - <<: *default - database: db/production.sqlite3 diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt new file mode 100644 index 0000000000..9510568124 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +# +default: &default + adapter: sqlite3 + pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml deleted file mode 100644 index 049de65f22..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml +++ /dev/null @@ -1,52 +0,0 @@ -# SQL Server (2012 or higher required) -# -# Install the adapters and driver -# gem install tiny_tds -# gem install activerecord-sqlserver-adapter -# -# Ensure the activerecord adapter and db driver gems are defined in your Gemfile -# gem 'tiny_tds' -# gem 'activerecord-sqlserver-adapter' -# -default: &default - adapter: sqlserver - encoding: utf8 - username: sa - password: <%%= ENV['SA_PASSWORD'] %> - host: localhost - -development: - <<: *default - database: <%= app_name %>_development - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: <%= app_name %>_test - -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="sqlserver://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%%= ENV['DATABASE_URL'] %> -# -production: - <<: *default - database: <%= app_name %>_production - username: <%= app_name %> - password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt new file mode 100644 index 0000000000..049de65f22 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt @@ -0,0 +1,52 @@ +# SQL Server (2012 or higher required) +# +# Install the adapters and driver +# gem install tiny_tds +# gem install activerecord-sqlserver-adapter +# +# Ensure the activerecord adapter and db driver gems are defined in your Gemfile +# gem 'tiny_tds' +# gem 'activerecord-sqlserver-adapter' +# +default: &default + adapter: sqlserver + encoding: utf8 + username: sa + password: <%%= ENV['SA_PASSWORD'] %> + host: localhost + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="sqlserver://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb deleted file mode 100644 index 426333bb46..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/environment.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Load the Rails application. -require_relative 'application' - -# Initialize the Rails application. -Rails.application.initialize! diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environment.rb.tt new file mode 100644 index 0000000000..426333bb46 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb.tt @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb deleted file mode 100644 index 89d2efab2b..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# ActiveSupport::Reloader.to_prepare do -# ApplicationController.renderer.defaults.merge!( -# http_host: 'example.org', -# https: false -# ) -# end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt new file mode 100644 index 0000000000..89d2efab2b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb deleted file mode 100644 index 59385cdf37..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. -# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } - -# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. -# Rails.backtrace_cleaner.remove_silencers! diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt new file mode 100644 index 0000000000..59385cdf37 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb deleted file mode 100644 index 5a6a32d371..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Specify a serializer for the signed and encrypted cookie jars. -# Valid options are :json, :marshal, and :hybrid. -Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt new file mode 100644 index 0000000000..5a6a32d371 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb deleted file mode 100644 index 3b1c1b5ed1..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb +++ /dev/null @@ -1,16 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Avoid CORS issues when API is called from the frontend app. -# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. - -# Read more: https://github.com/cyu/rack-cors - -# Rails.application.config.middleware.insert_before 0, Rack::Cors do -# allow do -# origins 'example.com' -# -# resource '*', -# headers: :any, -# methods: [:get, :post, :put, :patch, :delete, :options, :head] -# end -# end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt new file mode 100644 index 0000000000..3b1c1b5ed1 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Avoid CORS issues when API is called from the frontend app. +# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. + +# Read more: https://github.com/cyu/rack-cors + +# Rails.application.config.middleware.insert_before 0, Rack::Cors do +# allow do +# origins 'example.com' +# +# resource '*', +# headers: :any, +# methods: [:get, :post, :put, :patch, :delete, :options, :head] +# end +# end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb deleted file mode 100644 index 4a994e1e7b..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb +++ /dev/null @@ -1,4 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt new file mode 100644 index 0000000000..4a994e1e7b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb deleted file mode 100644 index ac033bf9dc..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb +++ /dev/null @@ -1,16 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Add new inflection rules using the following format. Inflections -# are locale specific, and you may define rules for as many different -# locales as you wish. All of these examples are active by default: -# ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' -# inflect.uncountable %w( fish sheep ) -# end - -# These inflection rules are supported but not enabled by default: -# ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' -# end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt new file mode 100644 index 0000000000..ac033bf9dc --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb deleted file mode 100644 index dc1899682b..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb +++ /dev/null @@ -1,4 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Add new mime types for use in respond_to blocks: -# Mime::Type.register "text/richtext", :rtf diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt new file mode 100644 index 0000000000..dc1899682b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/railties/lib/rails/generators/rails/app/templates/config/puma.rb b/railties/lib/rails/generators/rails/app/templates/config/puma.rb deleted file mode 100644 index 1e19380dcb..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/puma.rb +++ /dev/null @@ -1,56 +0,0 @@ -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. -# -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } -threads threads_count, threads_count - -# Specifies the `port` that Puma will listen on to receive requests; default is 3000. -# -port ENV.fetch("PORT") { 3000 } - -# Specifies the `environment` that Puma will run in. -# -environment ENV.fetch("RAILS_ENV") { "development" } - -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked webserver processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). -# -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } - -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. If you use this option -# you need to make sure to reconnect any threads in the `on_worker_boot` -# block. -# -# preload_app! - -# If you are preloading your application and using Active Record, it's -# recommended that you close any connections to the database before workers -# are forked to prevent connection leakage. -# -# before_fork do -# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) -# end - -# The code in the `on_worker_boot` will be called if you are using -# clustered mode by specifying a number of `workers`. After each worker -# process is booted, this block will be run. If you are using the `preload_app!` -# option, you will want to use this block to reconnect to any threads -# or connections that may have been created at application boot, as Ruby -# cannot share connections between processes. -# -# on_worker_boot do -# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) -# end -# - -# Allow puma to be restarted by `rails restart` command. -plugin :tmp_restart diff --git a/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt new file mode 100644 index 0000000000..1e19380dcb --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt @@ -0,0 +1,56 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# If you are preloading your application and using Active Record, it's +# recommended that you close any connections to the database before workers +# are forked to prevent connection leakage. +# +# before_fork do +# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) +# end + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted, this block will be run. If you are using the `preload_app!` +# option, you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, as Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end +# + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb deleted file mode 100644 index 787824f888..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ /dev/null @@ -1,3 +0,0 @@ -Rails.application.routes.draw do - # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html -end diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt new file mode 100644 index 0000000000..787824f888 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb.tt @@ -0,0 +1,3 @@ +Rails.application.routes.draw do + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html +end diff --git a/railties/lib/rails/generators/rails/app/templates/config/spring.rb b/railties/lib/rails/generators/rails/app/templates/config/spring.rb deleted file mode 100644 index 9fa7863f99..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/spring.rb +++ /dev/null @@ -1,6 +0,0 @@ -%w[ - .ruby-version - .rbenv-vars - tmp/restart.txt - tmp/caching-dev.txt -].each { |path| Spring.watch(path) } diff --git a/railties/lib/rails/generators/rails/app/templates/config/spring.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/spring.rb.tt new file mode 100644 index 0000000000..9fa7863f99 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/spring.rb.tt @@ -0,0 +1,6 @@ +%w[ + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +].each { |path| Spring.watch(path) } diff --git a/railties/lib/rails/generators/rails/app/templates/config/storage.yml b/railties/lib/rails/generators/rails/app/templates/config/storage.yml deleted file mode 100644 index 9bada4b66d..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/storage.yml +++ /dev/null @@ -1,35 +0,0 @@ -test: - service: Disk - root: <%%= Rails.root.join("tmp/storage") %> - -local: - service: Disk - root: <%%= Rails.root.join("storage") %> - -# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) -# amazon: -# service: S3 -# access_key_id: <%%= Rails.application.credentials.dig(:aws, :access_key_id) %> -# secret_access_key: <%%= Rails.application.credentials.dig(:aws, :secret_access_key) %> -# region: us-east-1 -# bucket: your_own_bucket - -# Remember not to checkin your GCS keyfile to a repository -# google: -# service: GCS -# project: your_project -# keyfile: <%%= Rails.root.join("path/to/gcs.keyfile") %> -# bucket: your_own_bucket - -# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) -# microsoft: -# service: AzureStorage -# path: your_azure_storage_path -# storage_account_name: your_account_name -# storage_access_key: <%%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> -# container: your_container_name - -# mirror: -# service: Mirror -# primary: local -# mirrors: [ amazon, google, microsoft ] diff --git a/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt new file mode 100644 index 0000000000..9bada4b66d --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt @@ -0,0 +1,35 @@ +test: + service: Disk + root: <%%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%%= Rails.root.join("storage") %> + +# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# keyfile: <%%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket + +# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# path: your_azure_storage_path +# storage_account_name: your_account_name +# storage_access_key: <%%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore deleted file mode 100644 index 2cd8335aba..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/gitignore +++ /dev/null @@ -1,37 +0,0 @@ -# See https://help.github.com/articles/ignoring-files for more about ignoring files. -# -# If you find yourself ignoring temporary files generated by your text editor -# or operating system, you probably want to add a global ignore instead: -# git config --global core.excludesfile '~/.gitignore_global' - -# Ignore bundler config. -/.bundle - -<% if sqlite3? -%> -# Ignore the default SQLite database. -/db/*.sqlite3 -/db/*.sqlite3-journal - -<% end -%> -# Ignore all logfiles and tempfiles. -/log/* -/tmp/* -<% if keeps? -%> -!/log/.keep -!/tmp/.keep -<% end -%> - -<% unless skip_active_storage? -%> -# Ignore uploaded files in development -/storage/* - -<% end -%> -<% unless options.skip_yarn? -%> -/node_modules -/yarn-error.log - -<% end -%> -<% unless options.api? -%> -/public/assets -<% end -%> -.byebug_history diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore.tt b/railties/lib/rails/generators/rails/app/templates/gitignore.tt new file mode 100644 index 0000000000..2cd8335aba --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/gitignore.tt @@ -0,0 +1,37 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +<% if sqlite3? -%> +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + +<% end -%> +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +<% if keeps? -%> +!/log/.keep +!/tmp/.keep +<% end -%> + +<% unless skip_active_storage? -%> +# Ignore uploaded files in development +/storage/* + +<% end -%> +<% unless options.skip_yarn? -%> +/node_modules +/yarn-error.log + +<% end -%> +<% unless options.api? -%> +/public/assets +<% end -%> +.byebug_history diff --git a/railties/lib/rails/generators/rails/app/templates/package.json b/railties/lib/rails/generators/rails/app/templates/package.json deleted file mode 100644 index 46db57dcbe..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "<%= app_name %>", - "private": true, - "dependencies": {} -} diff --git a/railties/lib/rails/generators/rails/app/templates/package.json.tt b/railties/lib/rails/generators/rails/app/templates/package.json.tt new file mode 100644 index 0000000000..46db57dcbe --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/package.json.tt @@ -0,0 +1,5 @@ +{ + "name": "<%= app_name %>", + "private": true, + "dependencies": {} +} diff --git a/railties/lib/rails/generators/rails/app/templates/ruby-version b/railties/lib/rails/generators/rails/app/templates/ruby-version deleted file mode 100644 index c444f33b0f..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/ruby-version +++ /dev/null @@ -1 +0,0 @@ -<%= RUBY_VERSION -%> diff --git a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt new file mode 100644 index 0000000000..c444f33b0f --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt @@ -0,0 +1 @@ +<%= RUBY_VERSION -%> diff --git a/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb b/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb deleted file mode 100644 index d19212abd5..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb +++ /dev/null @@ -1,5 +0,0 @@ -require "test_helper" - -class ApplicationSystemTestCase < ActionDispatch::SystemTestCase - driven_by :selenium, using: :chrome, screen_size: [1400, 1400] -end diff --git a/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt b/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt new file mode 100644 index 0000000000..d19212abd5 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :chrome, screen_size: [1400, 1400] +end diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb deleted file mode 100644 index 6ad1f11781..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb +++ /dev/null @@ -1,11 +0,0 @@ -require_relative '../config/environment' -require 'rails/test_help' - -class ActiveSupport::TestCase -<% unless options[:skip_active_record] -%> - # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. - fixtures :all - -<% end -%> - # Add more helper methods to be used by all tests here... -end diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt new file mode 100644 index 0000000000..6ad1f11781 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt @@ -0,0 +1,11 @@ +require_relative '../config/environment' +require 'rails/test_help' + +class ActiveSupport::TestCase +<% unless options[:skip_active_record] -%> + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + +<% end -%> + # Add more helper methods to be used by all tests here... +end diff --git a/railties/lib/rails/generators/rails/assets/templates/stylesheet.css b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css index 7594abf268..afad32db02 100644 --- a/railties/lib/rails/generators/rails/assets/templates/stylesheet.css +++ b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css @@ -1,4 +1,4 @@ -/* +/* Place all the styles related to the matching controller here. They will automatically be included in application.css. */ diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb deleted file mode 100644 index 633e0b3177..0000000000 --- a/railties/lib/rails/generators/rails/controller/templates/controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -<% if namespaced? -%> -require_dependency "<%= namespaced_path %>/application_controller" - -<% end -%> -<% module_namespacing do -%> -class <%= class_name %>Controller < ApplicationController -<% actions.each do |action| -%> - def <%= action %> - end -<%= "\n" unless action == actions.last -%> -<% end -%> -end -<% end -%> diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb.tt b/railties/lib/rails/generators/rails/controller/templates/controller.rb.tt new file mode 100644 index 0000000000..633e0b3177 --- /dev/null +++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb.tt @@ -0,0 +1,13 @@ +<% if namespaced? -%> +require_dependency "<%= namespaced_path %>/application_controller" + +<% end -%> +<% module_namespacing do -%> +class <%= class_name %>Controller < ApplicationController +<% actions.each do |action| -%> + def <%= action %> + end +<%= "\n" unless action == actions.last -%> +<% end -%> +end +<% end -%> diff --git a/railties/lib/rails/generators/rails/helper/templates/helper.rb b/railties/lib/rails/generators/rails/helper/templates/helper.rb deleted file mode 100644 index b4173151b4..0000000000 --- a/railties/lib/rails/generators/rails/helper/templates/helper.rb +++ /dev/null @@ -1,4 +0,0 @@ -<% module_namespacing do -%> -module <%= class_name %>Helper -end -<% end -%> diff --git a/railties/lib/rails/generators/rails/helper/templates/helper.rb.tt b/railties/lib/rails/generators/rails/helper/templates/helper.rb.tt new file mode 100644 index 0000000000..b4173151b4 --- /dev/null +++ b/railties/lib/rails/generators/rails/helper/templates/helper.rb.tt @@ -0,0 +1,4 @@ +<% module_namespacing do -%> +module <%= class_name %>Helper +end +<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec deleted file mode 100644 index 9a8c4bf098..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec +++ /dev/null @@ -1,24 +0,0 @@ -$:.push File.expand_path("lib", __dir__) - -# Maintain your gem's version: -require "<%= namespaced_name %>/version" - -# Describe your gem and declare its dependencies: -Gem::Specification.new do |s| - s.name = "<%= name %>" - s.version = <%= camelized_modules %>::VERSION - s.authors = ["<%= author %>"] - s.email = ["<%= email %>"] - s.homepage = "TODO" - s.summary = "TODO: Summary of <%= camelized_modules %>." - s.description = "TODO: Description of <%= camelized_modules %>." - s.license = "MIT" - - s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] - - <%= '# ' if options.dev? || options.edge? -%>s.add_dependency "rails", "<%= Array(rails_version_specifier).join('", "') %>" -<% unless options[:skip_active_record] -%> - - s.add_development_dependency "<%= gem_for_database[0] %>" -<% end -%> -end diff --git a/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt new file mode 100644 index 0000000000..9a8c4bf098 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt @@ -0,0 +1,24 @@ +$:.push File.expand_path("lib", __dir__) + +# Maintain your gem's version: +require "<%= namespaced_name %>/version" + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "<%= name %>" + s.version = <%= camelized_modules %>::VERSION + s.authors = ["<%= author %>"] + s.email = ["<%= email %>"] + s.homepage = "TODO" + s.summary = "TODO: Summary of <%= camelized_modules %>." + s.description = "TODO: Description of <%= camelized_modules %>." + s.license = "MIT" + + s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] + + <%= '# ' if options.dev? || options.edge? -%>s.add_dependency "rails", "<%= Array(rails_version_specifier).join('", "') %>" +<% unless options[:skip_active_record] -%> + + s.add_development_dependency "<%= gem_for_database[0] %>" +<% end -%> +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/Gemfile b/railties/lib/rails/generators/rails/plugin/templates/Gemfile deleted file mode 100644 index 290259b4db..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/Gemfile +++ /dev/null @@ -1,48 +0,0 @@ -source 'https://rubygems.org' -git_source(:github) { |repo| "https://github.com/#{repo}.git" } - -<% if options[:skip_gemspec] -%> -<%= '# ' if options.dev? || options.edge? -%>gem 'rails', '<%= Array(rails_version_specifier).join("', '") %>' -<% else -%> -# Declare your gem's dependencies in <%= name %>.gemspec. -# Bundler will treat runtime dependencies like base dependencies, and -# development dependencies will be added by default to the :development group. -gemspec -<% end -%> - -<% if options[:skip_gemspec] -%> -group :development do - gem '<%= gem_for_database[0] %>' -end -<% else -%> -# Declare any dependencies that are still in development here instead of in -# your gemspec. These might include edge Rails or gems from your path or -# Git. Remember to move these dependencies to your gemspec before releasing -# your gem to rubygems.org. -<% end -%> - -<% if options.dev? || options.edge? -%> -# Your gem is dependent on dev or edge Rails. Once you can lock this -# dependency down to a specific version, move it to your gemspec. -<% max_width = gemfile_entries.map { |g| g.name.length }.max -%> -<% gemfile_entries.each do |gem| -%> -<% if gem.comment -%> - -# <%= gem.comment %> -<% end -%> -<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%> -<% if gem.options.any? -%> -, <%= gem.options.map { |k,v| - "#{k}: #{v.inspect}" }.join(', ') %> -<% end -%> -<% end -%> - -<% end -%> -<% if RUBY_ENGINE == 'ruby' -%> -# To use a debugger -# gem 'byebug', group: [:development, :test] -<% end -%> -<% if RUBY_PLATFORM.match(/bccwin|cygwin|emx|mingw|mswin|wince|java/) -%> - -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] -<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/Gemfile.tt b/railties/lib/rails/generators/rails/plugin/templates/Gemfile.tt new file mode 100644 index 0000000000..290259b4db --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/Gemfile.tt @@ -0,0 +1,48 @@ +source 'https://rubygems.org' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +<% if options[:skip_gemspec] -%> +<%= '# ' if options.dev? || options.edge? -%>gem 'rails', '<%= Array(rails_version_specifier).join("', '") %>' +<% else -%> +# Declare your gem's dependencies in <%= name %>.gemspec. +# Bundler will treat runtime dependencies like base dependencies, and +# development dependencies will be added by default to the :development group. +gemspec +<% end -%> + +<% if options[:skip_gemspec] -%> +group :development do + gem '<%= gem_for_database[0] %>' +end +<% else -%> +# Declare any dependencies that are still in development here instead of in +# your gemspec. These might include edge Rails or gems from your path or +# Git. Remember to move these dependencies to your gemspec before releasing +# your gem to rubygems.org. +<% end -%> + +<% if options.dev? || options.edge? -%> +# Your gem is dependent on dev or edge Rails. Once you can lock this +# dependency down to a specific version, move it to your gemspec. +<% max_width = gemfile_entries.map { |g| g.name.length }.max -%> +<% gemfile_entries.each do |gem| -%> +<% if gem.comment -%> + +# <%= gem.comment %> +<% end -%> +<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%> +<% if gem.options.any? -%> +, <%= gem.options.map { |k,v| + "#{k}: #{v.inspect}" }.join(', ') %> +<% end -%> +<% end -%> + +<% end -%> +<% if RUBY_ENGINE == 'ruby' -%> +# To use a debugger +# gem 'byebug', group: [:development, :test] +<% end -%> +<% if RUBY_PLATFORM.match(/bccwin|cygwin|emx|mingw|mswin|wince|java/) -%> + +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE deleted file mode 100644 index ff2fb3ba4e..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright <%= Date.today.year %> <%= author %> - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt new file mode 100644 index 0000000000..ff2fb3ba4e --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt @@ -0,0 +1,20 @@ +Copyright <%= Date.today.year %> <%= author %> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/railties/lib/rails/generators/rails/plugin/templates/README.md b/railties/lib/rails/generators/rails/plugin/templates/README.md deleted file mode 100644 index 1632409bea..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# <%= camelized_modules %> -Short description and motivation. - -## Usage -How to use my plugin. - -## Installation -Add this line to your application's Gemfile: - -```ruby -gem '<%= name %>' -``` - -And then execute: -```bash -$ bundle -``` - -Or install it yourself as: -```bash -$ gem install <%= name %> -``` - -## Contributing -Contribution directions go here. - -## License -The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). diff --git a/railties/lib/rails/generators/rails/plugin/templates/README.md.tt b/railties/lib/rails/generators/rails/plugin/templates/README.md.tt new file mode 100644 index 0000000000..1632409bea --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/README.md.tt @@ -0,0 +1,28 @@ +# <%= camelized_modules %> +Short description and motivation. + +## Usage +How to use my plugin. + +## Installation +Add this line to your application's Gemfile: + +```ruby +gem '<%= name %>' +``` + +And then execute: +```bash +$ bundle +``` + +Or install it yourself as: +```bash +$ gem install <%= name %> +``` + +## Contributing +Contribution directions go here. + +## License +The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile deleted file mode 100644 index f3efe21cf1..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile +++ /dev/null @@ -1,28 +0,0 @@ -begin - require 'bundler/setup' -rescue LoadError - puts 'You must `gem install bundler` and `bundle install` to run rake tasks' -end - -require 'rdoc/task' - -RDoc::Task.new(:rdoc) do |rdoc| - rdoc.rdoc_dir = 'rdoc' - rdoc.title = '<%= camelized_modules %>' - rdoc.options << '--line-numbers' - rdoc.rdoc_files.include('README.md') - rdoc.rdoc_files.include('lib/**/*.rb') -end -<% if engine? && !options[:skip_active_record] && with_dummy_app? -%> - -APP_RAKEFILE = File.expand_path("<%= dummy_path -%>/Rakefile", __dir__) -load 'rails/tasks/engine.rake' -<% end -%> -<% if engine? -%> - -load 'rails/tasks/statistics.rake' -<% end -%> -<% unless options[:skip_gemspec] -%> - -require 'bundler/gem_tasks' -<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt b/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt new file mode 100644 index 0000000000..f3efe21cf1 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt @@ -0,0 +1,28 @@ +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end + +require 'rdoc/task' + +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = '<%= camelized_modules %>' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.md') + rdoc.rdoc_files.include('lib/**/*.rb') +end +<% if engine? && !options[:skip_active_record] && with_dummy_app? -%> + +APP_RAKEFILE = File.expand_path("<%= dummy_path -%>/Rakefile", __dir__) +load 'rails/tasks/engine.rake' +<% end -%> +<% if engine? -%> + +load 'rails/tasks/statistics.rake' +<% end -%> +<% unless options[:skip_gemspec] -%> + +require 'bundler/gem_tasks' +<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb deleted file mode 100644 index 154452bfe5..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb +++ /dev/null @@ -1,6 +0,0 @@ -<% if mountable? -%> -<%= camelized_modules %>::Engine.routes.draw do -<% else -%> -Rails.application.routes.draw do -<% end -%> -end diff --git a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb.tt new file mode 100644 index 0000000000..154452bfe5 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb.tt @@ -0,0 +1,6 @@ +<% if mountable? -%> +<%= camelized_modules %>::Engine.routes.draw do +<% else -%> +Rails.application.routes.draw do +<% end -%> +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore deleted file mode 100644 index 7a68da5c4b..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/gitignore +++ /dev/null @@ -1,18 +0,0 @@ -.bundle/ -log/*.log -pkg/ -<% if with_dummy_app? -%> -<% if sqlite3? -%> -<%= dummy_path %>/db/*.sqlite3 -<%= dummy_path %>/db/*.sqlite3-journal -<% end -%> -<%= dummy_path %>/log/*.log -<% unless options[:skip_yarn] -%> -<%= dummy_path %>/node_modules/ -<%= dummy_path %>/yarn-error.log -<% end -%> -<% unless skip_active_storage? -%> -<%= dummy_path %>/storage/ -<% end -%> -<%= dummy_path %>/tmp/ -<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt b/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt new file mode 100644 index 0000000000..7a68da5c4b --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore.tt @@ -0,0 +1,18 @@ +.bundle/ +log/*.log +pkg/ +<% if with_dummy_app? -%> +<% if sqlite3? -%> +<%= dummy_path %>/db/*.sqlite3 +<%= dummy_path %>/db/*.sqlite3-journal +<% end -%> +<%= dummy_path %>/log/*.log +<% unless options[:skip_yarn] -%> +<%= dummy_path %>/node_modules/ +<%= dummy_path %>/yarn-error.log +<% end -%> +<% unless skip_active_storage? -%> +<%= dummy_path %>/storage/ +<% end -%> +<%= dummy_path %>/tmp/ +<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb deleted file mode 100644 index 3285055eb7..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb +++ /dev/null @@ -1,7 +0,0 @@ -<% if engine? -%> -require "<%= namespaced_name %>/engine" -<% else -%> -require "<%= namespaced_name %>/railtie" -<% end -%> - -<%= wrap_in_modules "# Your code goes here..." %> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt new file mode 100644 index 0000000000..3285055eb7 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt @@ -0,0 +1,7 @@ +<% if engine? -%> +require "<%= namespaced_name %>/engine" +<% else -%> +require "<%= namespaced_name %>/railtie" +<% end -%> + +<%= wrap_in_modules "# Your code goes here..." %> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb deleted file mode 100644 index 8938770fc4..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb +++ /dev/null @@ -1,7 +0,0 @@ -<%= wrap_in_modules <<-rb.strip_heredoc - class Engine < ::Rails::Engine - #{mountable? ? ' isolate_namespace ' + camelized_modules : ' '} - #{api? ? " config.generators.api_only = true" : ' '} - end -rb -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt new file mode 100644 index 0000000000..8938770fc4 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt @@ -0,0 +1,7 @@ +<%= wrap_in_modules <<-rb.strip_heredoc + class Engine < ::Rails::Engine + #{mountable? ? ' isolate_namespace ' + camelized_modules : ' '} + #{api? ? " config.generators.api_only = true" : ' '} + end +rb +%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb deleted file mode 100644 index 7bdf4ee5fb..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb +++ /dev/null @@ -1,5 +0,0 @@ -<%= wrap_in_modules <<-rb.strip_heredoc - class Railtie < ::Rails::Railtie - end -rb -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt new file mode 100644 index 0000000000..7bdf4ee5fb --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt @@ -0,0 +1,5 @@ +<%= wrap_in_modules <<-rb.strip_heredoc + class Railtie < ::Rails::Railtie + end +rb +%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb deleted file mode 100644 index b08f4ef9ae..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb +++ /dev/null @@ -1 +0,0 @@ -<%= wrap_in_modules "VERSION = '0.1.0'" %> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt new file mode 100644 index 0000000000..b08f4ef9ae --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt @@ -0,0 +1 @@ +<%= wrap_in_modules "VERSION = '0.1.0'" %> diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake deleted file mode 100644 index 88a2c4120f..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake +++ /dev/null @@ -1,4 +0,0 @@ -# desc "Explaining what the task does" -# task :<%= underscored_name %> do -# # Task goes here -# end diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt new file mode 100644 index 0000000000..88a2c4120f --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :<%= underscored_name %> do +# # Task goes here +# end diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb deleted file mode 100644 index 06ffe2f1ed..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb +++ /dev/null @@ -1,23 +0,0 @@ -require_relative 'boot' - -<% if include_all_railties? -%> -require 'rails/all' -<% else -%> -require "rails" -# Pick the frameworks you want: -require "active_model/railtie" -require "active_job/railtie" -<%= comment_if :skip_active_record %>require "active_record/railtie" -<%= comment_if :skip_active_storage %>require "active_storage/engine" -require "action_controller/railtie" -<%= comment_if :skip_action_mailer %>require "action_mailer/railtie" -require "action_view/railtie" -<%= comment_if :skip_action_cable %>require "action_cable/engine" -<%= comment_if :skip_sprockets %>require "sprockets/railtie" -<%= comment_if :skip_test %>require "rails/test_unit/railtie" -<% end -%> - -Bundler.require(*Rails.groups) -require "<%= namespaced_name %>" - -<%= application_definition %> diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt new file mode 100644 index 0000000000..06ffe2f1ed --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt @@ -0,0 +1,23 @@ +require_relative 'boot' + +<% if include_all_railties? -%> +require 'rails/all' +<% else -%> +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +<%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" +require "action_controller/railtie" +<%= comment_if :skip_action_mailer %>require "action_mailer/railtie" +require "action_view/railtie" +<%= comment_if :skip_action_cable %>require "action_cable/engine" +<%= comment_if :skip_sprockets %>require "sprockets/railtie" +<%= comment_if :skip_test %>require "rails/test_unit/railtie" +<% end -%> + +Bundler.require(*Rails.groups) +require "<%= namespaced_name %>" + +<%= application_definition %> diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb deleted file mode 100644 index c9aef85d40..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Set up gems listed in the Gemfile. -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) - -require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) -$LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt new file mode 100644 index 0000000000..c9aef85d40 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt @@ -0,0 +1,5 @@ +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) + +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +$LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js deleted file mode 100644 index 03937cf8ff..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js +++ /dev/null @@ -1,10 +0,0 @@ -<% unless api? -%> -//= link_tree ../images -<% end -%> -<% unless options.skip_javascript -%> -//= link_directory ../javascripts .js -<% end -%> -//= link_directory ../stylesheets .css -<% if mountable? && !api? -%> -//= link <%= underscored_name %>_manifest.js -<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js.tt b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js.tt new file mode 100644 index 0000000000..03937cf8ff --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js.tt @@ -0,0 +1,10 @@ +<% unless api? -%> +//= link_tree ../images +<% end -%> +<% unless options.skip_javascript -%> +//= link_directory ../javascripts .js +<% end -%> +//= link_directory ../stylesheets .css +<% if mountable? && !api? -%> +//= link <%= underscored_name %>_manifest.js +<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js deleted file mode 100644 index 2f23844f5e..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js +++ /dev/null @@ -1,6 +0,0 @@ -<% if mountable? -%> -<% if !options.skip_javascript -%> -//= link_directory ../javascripts/<%= namespaced_name %> .js -<% end -%> -//= link_directory ../stylesheets/<%= namespaced_name %> .css -<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js.tt b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js.tt new file mode 100644 index 0000000000..2f23844f5e --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js.tt @@ -0,0 +1,6 @@ +<% if mountable? -%> +<% if !options.skip_javascript -%> +//= link_directory ../javascripts/<%= namespaced_name %> .js +<% end -%> +//= link_directory ../stylesheets/<%= namespaced_name %> .css +<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js deleted file mode 100644 index f3d80c87f5..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js +++ /dev/null @@ -1,16 +0,0 @@ -// This is a manifest file that'll be compiled into application.js, which will include all the files -// listed below. -// -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. -// -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// compiled file. JavaScript code in this file should be added after the last require_* statement. -// -// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details -// about supported directives. -// -<% unless skip_active_storage? -%> -//= require activestorage -<% end -%> -//= require_tree . diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js.tt b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js.tt new file mode 100644 index 0000000000..f3d80c87f5 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js.tt @@ -0,0 +1,16 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +<% unless skip_active_storage? -%> +//= require activestorage +<% end -%> +//= require_tree . diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb deleted file mode 100644 index 694510edc0..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb +++ /dev/null @@ -1,3 +0,0 @@ -Rails.application.routes.draw do - mount <%= camelized_modules %>::Engine => "/<%= name %>" -end diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb.tt new file mode 100644 index 0000000000..694510edc0 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb.tt @@ -0,0 +1,3 @@ +Rails.application.routes.draw do + mount <%= camelized_modules %>::Engine => "/<%= name %>" +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb deleted file mode 100644 index 1ee05d7871..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class <%= camelized_modules %>::Test < ActiveSupport::TestCase - test "truth" do - assert_kind_of Module, <%= camelized_modules %> - end -end diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt new file mode 100644 index 0000000000..1ee05d7871 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt @@ -0,0 +1,7 @@ +require 'test_helper' + +class <%= camelized_modules %>::Test < ActiveSupport::TestCase + test "truth" do + assert_kind_of Module, <%= camelized_modules %> + end +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb b/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb deleted file mode 100644 index d19212abd5..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb +++ /dev/null @@ -1,5 +0,0 @@ -require "test_helper" - -class ApplicationSystemTestCase < ActionDispatch::SystemTestCase - driven_by :selenium, using: :chrome, screen_size: [1400, 1400] -end diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt new file mode 100644 index 0000000000..d19212abd5 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :chrome, screen_size: [1400, 1400] +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb deleted file mode 100644 index 29e59d8407..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class NavigationTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt new file mode 100644 index 0000000000..29e59d8407 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt @@ -0,0 +1,7 @@ +require 'test_helper' + +class NavigationTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb deleted file mode 100644 index 7fa9973931..0000000000 --- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb +++ /dev/null @@ -1,27 +0,0 @@ -require_relative "<%= File.join('..', options[:dummy_path], 'config/environment') -%>" -<% unless options[:skip_active_record] -%> -ActiveRecord::Migrator.migrations_paths = [File.expand_path("../<%= options[:dummy_path] -%>/db/migrate", __dir__)] -<% if options[:mountable] -%> -ActiveRecord::Migrator.migrations_paths << File.expand_path('../db/migrate', __dir__) -<% end -%> -<% end -%> -require "rails/test_help" - -# Filter out Minitest backtrace while allowing backtrace from other libraries -# to be shown. -Minitest.backtrace_filter = Minitest::BacktraceFilter.new - -<% unless engine? -%> -require "rails/test_unit/reporter" -Rails::TestUnitReporter.executable = 'bin/test' -<% end -%> - -<% unless options[:skip_active_record] -%> -# Load fixtures from the engine -if ActiveSupport::TestCase.respond_to?(:fixture_path=) - ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__) - ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path - ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" - ActiveSupport::TestCase.fixtures :all -end -<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt new file mode 100644 index 0000000000..7fa9973931 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt @@ -0,0 +1,27 @@ +require_relative "<%= File.join('..', options[:dummy_path], 'config/environment') -%>" +<% unless options[:skip_active_record] -%> +ActiveRecord::Migrator.migrations_paths = [File.expand_path("../<%= options[:dummy_path] -%>/db/migrate", __dir__)] +<% if options[:mountable] -%> +ActiveRecord::Migrator.migrations_paths << File.expand_path('../db/migrate', __dir__) +<% end -%> +<% end -%> +require "rails/test_help" + +# Filter out Minitest backtrace while allowing backtrace from other libraries +# to be shown. +Minitest.backtrace_filter = Minitest::BacktraceFilter.new + +<% unless engine? -%> +require "rails/test_unit/reporter" +Rails::TestUnitReporter.executable = 'bin/test' +<% end -%> + +<% unless options[:skip_active_record] -%> +# Load fixtures from the engine +if ActiveSupport::TestCase.respond_to?(:fixture_path=) + ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__) + ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path + ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" + ActiveSupport::TestCase.fixtures :all +end +<% end -%> diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb deleted file mode 100644 index 400afec6dc..0000000000 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb +++ /dev/null @@ -1,61 +0,0 @@ -<% if namespaced? -%> -require_dependency "<%= namespaced_path %>/application_controller" - -<% end -%> -<% module_namespacing do -%> -class <%= controller_class_name %>Controller < ApplicationController - before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy] - - # GET <%= route_url %> - def index - @<%= plural_table_name %> = <%= orm_class.all(class_name) %> - - render json: <%= "@#{plural_table_name}" %> - end - - # GET <%= route_url %>/1 - def show - render json: <%= "@#{singular_table_name}" %> - end - - # POST <%= route_url %> - def create - @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> - - if @<%= orm_instance.save %> - render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %> - else - render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity - end - end - - # PATCH/PUT <%= route_url %>/1 - def update - if @<%= orm_instance.update("#{singular_table_name}_params") %> - render json: <%= "@#{singular_table_name}" %> - else - render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity - end - end - - # DELETE <%= route_url %>/1 - def destroy - @<%= orm_instance.destroy %> - end - - private - # Use callbacks to share common setup or constraints between actions. - def set_<%= singular_table_name %> - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> - end - - # Only allow a trusted parameter "white list" through. - def <%= "#{singular_table_name}_params" %> - <%- if attributes_names.empty? -%> - params.fetch(:<%= singular_table_name %>, {}) - <%- else -%> - params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) - <%- end -%> - end -end -<% end -%> diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt new file mode 100644 index 0000000000..400afec6dc --- /dev/null +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt @@ -0,0 +1,61 @@ +<% if namespaced? -%> +require_dependency "<%= namespaced_path %>/application_controller" + +<% end -%> +<% module_namespacing do -%> +class <%= controller_class_name %>Controller < ApplicationController + before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy] + + # GET <%= route_url %> + def index + @<%= plural_table_name %> = <%= orm_class.all(class_name) %> + + render json: <%= "@#{plural_table_name}" %> + end + + # GET <%= route_url %>/1 + def show + render json: <%= "@#{singular_table_name}" %> + end + + # POST <%= route_url %> + def create + @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> + + if @<%= orm_instance.save %> + render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %> + else + render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity + end + end + + # PATCH/PUT <%= route_url %>/1 + def update + if @<%= orm_instance.update("#{singular_table_name}_params") %> + render json: <%= "@#{singular_table_name}" %> + else + render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity + end + end + + # DELETE <%= route_url %>/1 + def destroy + @<%= orm_instance.destroy %> + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_<%= singular_table_name %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> + end + + # Only allow a trusted parameter "white list" through. + def <%= "#{singular_table_name}_params" %> + <%- if attributes_names.empty? -%> + params.fetch(:<%= singular_table_name %>, {}) + <%- else -%> + params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) + <%- end -%> + end +end +<% end -%> diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb deleted file mode 100644 index 05f1c2b2d3..0000000000 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +++ /dev/null @@ -1,68 +0,0 @@ -<% if namespaced? -%> -require_dependency "<%= namespaced_path %>/application_controller" - -<% end -%> -<% module_namespacing do -%> -class <%= controller_class_name %>Controller < ApplicationController - before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy] - - # GET <%= route_url %> - def index - @<%= plural_table_name %> = <%= orm_class.all(class_name) %> - end - - # GET <%= route_url %>/1 - def show - end - - # GET <%= route_url %>/new - def new - @<%= singular_table_name %> = <%= orm_class.build(class_name) %> - end - - # GET <%= route_url %>/1/edit - def edit - end - - # POST <%= route_url %> - def create - @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> - - if @<%= orm_instance.save %> - redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully created.'" %> - else - render :new - end - end - - # PATCH/PUT <%= route_url %>/1 - def update - if @<%= orm_instance.update("#{singular_table_name}_params") %> - redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> - else - render :edit - end - end - - # DELETE <%= route_url %>/1 - def destroy - @<%= orm_instance.destroy %> - redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %> - end - - private - # Use callbacks to share common setup or constraints between actions. - def set_<%= singular_table_name %> - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> - end - - # Only allow a trusted parameter "white list" through. - def <%= "#{singular_table_name}_params" %> - <%- if attributes_names.empty? -%> - params.fetch(:<%= singular_table_name %>, {}) - <%- else -%> - params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) - <%- end -%> - end -end -<% end -%> diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt new file mode 100644 index 0000000000..05f1c2b2d3 --- /dev/null +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt @@ -0,0 +1,68 @@ +<% if namespaced? -%> +require_dependency "<%= namespaced_path %>/application_controller" + +<% end -%> +<% module_namespacing do -%> +class <%= controller_class_name %>Controller < ApplicationController + before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy] + + # GET <%= route_url %> + def index + @<%= plural_table_name %> = <%= orm_class.all(class_name) %> + end + + # GET <%= route_url %>/1 + def show + end + + # GET <%= route_url %>/new + def new + @<%= singular_table_name %> = <%= orm_class.build(class_name) %> + end + + # GET <%= route_url %>/1/edit + def edit + end + + # POST <%= route_url %> + def create + @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> + + if @<%= orm_instance.save %> + redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully created.'" %> + else + render :new + end + end + + # PATCH/PUT <%= route_url %>/1 + def update + if @<%= orm_instance.update("#{singular_table_name}_params") %> + redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> + else + render :edit + end + end + + # DELETE <%= route_url %>/1 + def destroy + @<%= orm_instance.destroy %> + redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %> + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_<%= singular_table_name %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> + end + + # Only allow a trusted parameter "white list" through. + def <%= "#{singular_table_name}_params" %> + <%- if attributes_names.empty? -%> + params.fetch(:<%= singular_table_name %>, {}) + <%- else -%> + params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) + <%- end -%> + end +end +<% end -%> diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb b/railties/lib/rails/generators/rails/task/templates/task.rb deleted file mode 100644 index 1e3ed5f158..0000000000 --- a/railties/lib/rails/generators/rails/task/templates/task.rb +++ /dev/null @@ -1,8 +0,0 @@ -namespace :<%= file_name %> do -<% actions.each do |action| -%> - desc "TODO" - task <%= action %>: :environment do - end - -<% end -%> -end diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb.tt b/railties/lib/rails/generators/rails/task/templates/task.rb.tt new file mode 100644 index 0000000000..1e3ed5f158 --- /dev/null +++ b/railties/lib/rails/generators/rails/task/templates/task.rb.tt @@ -0,0 +1,8 @@ +namespace :<%= file_name %> do +<% actions.each do |action| -%> + desc "TODO" + task <%= action %>: :environment do + end + +<% end -%> +end diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb deleted file mode 100644 index ff41fef9e9..0000000000 --- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= class_name %>ControllerTest < ActionDispatch::IntegrationTest -<% if mountable_engine? -%> - include Engine.routes.url_helpers - -<% end -%> -<% if actions.empty? -%> - # test "the truth" do - # assert true - # end -<% else -%> -<% actions.each do |action| -%> - test "should get <%= action %>" do - get <%= url_helper_prefix %>_<%= action %>_url - assert_response :success - end - -<% end -%> -<% end -%> -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt new file mode 100644 index 0000000000..ff41fef9e9 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt @@ -0,0 +1,23 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>ControllerTest < ActionDispatch::IntegrationTest +<% if mountable_engine? -%> + include Engine.routes.url_helpers + +<% end -%> +<% if actions.empty? -%> + # test "the truth" do + # assert true + # end +<% else -%> +<% actions.each do |action| -%> + test "should get <%= action %>" do + get <%= url_helper_prefix %>_<%= action %>_url + assert_response :success + end + +<% end -%> +<% end -%> +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb deleted file mode 100644 index a7f1fc4fba..0000000000 --- a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'test_helper' -require '<%= generator_path %>' - -<% module_namespacing do -%> -class <%= class_name %>GeneratorTest < Rails::Generators::TestCase - tests <%= class_name %>Generator - destination Rails.root.join('tmp/generators') - setup :prepare_destination - - # test "generator runs without errors" do - # assert_nothing_raised do - # run_generator ["arguments"] - # end - # end -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt new file mode 100644 index 0000000000..a7f1fc4fba --- /dev/null +++ b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt @@ -0,0 +1,16 @@ +require 'test_helper' +require '<%= generator_path %>' + +<% module_namespacing do -%> +class <%= class_name %>GeneratorTest < Rails::Generators::TestCase + tests <%= class_name %>Generator + destination Rails.root.join('tmp/generators') + setup :prepare_destination + + # test "generator runs without errors" do + # assert_nothing_raised do + # run_generator ["arguments"] + # end + # end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb deleted file mode 100644 index 118e0f1271..0000000000 --- a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= class_name %>Test < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt new file mode 100644 index 0000000000..118e0f1271 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt @@ -0,0 +1,9 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>Test < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/job/job_generator.rb b/railties/lib/rails/generators/test_unit/job/job_generator.rb index 9225af4e0c..a99ce914c0 100644 --- a/railties/lib/rails/generators/test_unit/job/job_generator.rb +++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb @@ -8,7 +8,7 @@ module TestUnit # :nodoc: check_class_collision suffix: "JobTest" def create_test_file - template "unit_test.rb.erb", File.join("test/jobs", class_path, "#{file_name}_job_test.rb") + template "unit_test.rb", File.join("test/jobs", class_path, "#{file_name}_job_test.rb") end end end diff --git a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb deleted file mode 100644 index f5351d0ec6..0000000000 --- a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= class_name %>JobTest < ActiveJob::TestCase - # test "the truth" do - # assert true - # end -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt new file mode 100644 index 0000000000..f5351d0ec6 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt @@ -0,0 +1,9 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>JobTest < ActiveJob::TestCase + # test "the truth" do + # assert true + # end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb deleted file mode 100644 index a2f2d30de5..0000000000 --- a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= class_name %>MailerTest < ActionMailer::TestCase -<% actions.each do |action| -%> - test "<%= action %>" do - mail = <%= class_name %>Mailer.<%= action %> - assert_equal <%= action.to_s.humanize.inspect %>, mail.subject - assert_equal ["to@example.org"], mail.to - assert_equal ["from@example.com"], mail.from - assert_match "Hi", mail.body.encoded - end - -<% end -%> -<% if actions.blank? -%> - # test "the truth" do - # assert true - # end -<% end -%> -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt new file mode 100644 index 0000000000..a2f2d30de5 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt @@ -0,0 +1,21 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>MailerTest < ActionMailer::TestCase +<% actions.each do |action| -%> + test "<%= action %>" do + mail = <%= class_name %>Mailer.<%= action %> + assert_equal <%= action.to_s.humanize.inspect %>, mail.subject + assert_equal ["to@example.org"], mail.to + assert_equal ["from@example.com"], mail.from + assert_match "Hi", mail.body.encoded + end + +<% end -%> +<% if actions.blank? -%> + # test "the truth" do + # assert true + # end +<% end -%> +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb deleted file mode 100644 index b063cbc47b..0000000000 --- a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb +++ /dev/null @@ -1,13 +0,0 @@ -<% module_namespacing do -%> -# Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %>_mailer -class <%= class_name %>MailerPreview < ActionMailer::Preview -<% actions.each do |action| -%> - - # Preview this email at http://localhost:3000/rails/mailers/<%= file_path %>_mailer/<%= action %> - def <%= action %> - <%= class_name %>Mailer.<%= action %> - end -<% end -%> - -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb.tt b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb.tt new file mode 100644 index 0000000000..b063cbc47b --- /dev/null +++ b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb.tt @@ -0,0 +1,13 @@ +<% module_namespacing do -%> +# Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %>_mailer +class <%= class_name %>MailerPreview < ActionMailer::Preview +<% actions.each do |action| -%> + + # Preview this email at http://localhost:3000/rails/mailers/<%= file_path %>_mailer/<%= action %> + def <%= action %> + <%= class_name %>Mailer.<%= action %> + end +<% end -%> + +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml deleted file mode 100644 index 0681780c97..0000000000 --- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml +++ /dev/null @@ -1,29 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -<% unless attributes.empty? -%> -<% %w(one two).each do |name| %> -<%= name %>: -<% attributes.each do |attribute| -%> - <%- if attribute.password_digest? -%> - password_digest: <%%= BCrypt::Password.create('secret') %> - <%- elsif attribute.reference? -%> - <%= yaml_key_value(attribute.column_name.sub(/_id$/, ''), attribute.default || name) %> - <%- else -%> - <%= yaml_key_value(attribute.column_name, attribute.default) %> - <%- end -%> - <%- if attribute.polymorphic? -%> - <%= yaml_key_value("#{attribute.name}_type", attribute.human_name) %> - <%- end -%> -<% end -%> -<% end -%> -<% else -%> - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt new file mode 100644 index 0000000000..0681780c97 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt @@ -0,0 +1,29 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html +<% unless attributes.empty? -%> +<% %w(one two).each do |name| %> +<%= name %>: +<% attributes.each do |attribute| -%> + <%- if attribute.password_digest? -%> + password_digest: <%%= BCrypt::Password.create('secret') %> + <%- elsif attribute.reference? -%> + <%= yaml_key_value(attribute.column_name.sub(/_id$/, ''), attribute.default || name) %> + <%- else -%> + <%= yaml_key_value(attribute.column_name, attribute.default) %> + <%- end -%> + <%- if attribute.polymorphic? -%> + <%= yaml_key_value("#{attribute.name}_type", attribute.human_name) %> + <%- end -%> +<% end -%> +<% end -%> +<% else -%> + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb deleted file mode 100644 index c9bc7d5b90..0000000000 --- a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= class_name %>Test < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt new file mode 100644 index 0000000000..c9bc7d5b90 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt @@ -0,0 +1,9 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>Test < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb deleted file mode 100644 index f21861d8e6..0000000000 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTest - <%- if mountable_engine? -%> - include Engine.routes.url_helpers - - <%- end -%> - setup do - @<%= singular_table_name %> = <%= fixture_name %>(:one) - end - - test "should get index" do - get <%= index_helper %>_url, as: :json - assert_response :success - end - - test "should create <%= singular_table_name %>" do - assert_difference('<%= class_name %>.count') do - post <%= index_helper %>_url, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> }, as: :json - end - - assert_response 201 - end - - test "should show <%= singular_table_name %>" do - get <%= show_helper %>, as: :json - assert_response :success - end - - test "should update <%= singular_table_name %>" do - patch <%= show_helper %>, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> }, as: :json - assert_response 200 - end - - test "should destroy <%= singular_table_name %>" do - assert_difference('<%= class_name %>.count', -1) do - delete <%= show_helper %>, as: :json - end - - assert_response 204 - end -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt new file mode 100644 index 0000000000..f21861d8e6 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt @@ -0,0 +1,44 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTest + <%- if mountable_engine? -%> + include Engine.routes.url_helpers + + <%- end -%> + setup do + @<%= singular_table_name %> = <%= fixture_name %>(:one) + end + + test "should get index" do + get <%= index_helper %>_url, as: :json + assert_response :success + end + + test "should create <%= singular_table_name %>" do + assert_difference('<%= class_name %>.count') do + post <%= index_helper %>_url, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> }, as: :json + end + + assert_response 201 + end + + test "should show <%= singular_table_name %>" do + get <%= show_helper %>, as: :json + assert_response :success + end + + test "should update <%= singular_table_name %>" do + patch <%= show_helper %>, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> }, as: :json + assert_response 200 + end + + test "should destroy <%= singular_table_name %>" do + assert_difference('<%= class_name %>.count', -1) do + delete <%= show_helper %>, as: :json + end + + assert_response 204 + end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb deleted file mode 100644 index 195d60be20..0000000000 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTest - <%- if mountable_engine? -%> - include Engine.routes.url_helpers - - <%- end -%> - setup do - @<%= singular_table_name %> = <%= fixture_name %>(:one) - end - - test "should get index" do - get <%= index_helper %>_url - assert_response :success - end - - test "should get new" do - get <%= new_helper %> - assert_response :success - end - - test "should create <%= singular_table_name %>" do - assert_difference('<%= class_name %>.count') do - post <%= index_helper %>_url, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> } - end - - assert_redirected_to <%= singular_table_name %>_url(<%= class_name %>.last) - end - - test "should show <%= singular_table_name %>" do - get <%= show_helper %> - assert_response :success - end - - test "should get edit" do - get <%= edit_helper %> - assert_response :success - end - - test "should update <%= singular_table_name %>" do - patch <%= show_helper %>, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> } - assert_redirected_to <%= singular_table_name %>_url(<%= "@#{singular_table_name}" %>) - end - - test "should destroy <%= singular_table_name %>" do - assert_difference('<%= class_name %>.count', -1) do - delete <%= show_helper %> - end - - assert_redirected_to <%= index_helper %>_url - end -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt new file mode 100644 index 0000000000..195d60be20 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt @@ -0,0 +1,54 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTest + <%- if mountable_engine? -%> + include Engine.routes.url_helpers + + <%- end -%> + setup do + @<%= singular_table_name %> = <%= fixture_name %>(:one) + end + + test "should get index" do + get <%= index_helper %>_url + assert_response :success + end + + test "should get new" do + get <%= new_helper %> + assert_response :success + end + + test "should create <%= singular_table_name %>" do + assert_difference('<%= class_name %>.count') do + post <%= index_helper %>_url, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> } + end + + assert_redirected_to <%= singular_table_name %>_url(<%= class_name %>.last) + end + + test "should show <%= singular_table_name %>" do + get <%= show_helper %> + assert_response :success + end + + test "should get edit" do + get <%= edit_helper %> + assert_response :success + end + + test "should update <%= singular_table_name %>" do + patch <%= show_helper %>, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> } + assert_redirected_to <%= singular_table_name %>_url(<%= "@#{singular_table_name}" %>) + end + + test "should destroy <%= singular_table_name %>" do + assert_difference('<%= class_name %>.count', -1) do + delete <%= show_helper %> + end + + assert_redirected_to <%= index_helper %>_url + end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb deleted file mode 100644 index f83f5a5c62..0000000000 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb +++ /dev/null @@ -1,49 +0,0 @@ -require "application_system_test_case" - -<% module_namespacing do -%> -class <%= class_name.pluralize %>Test < ApplicationSystemTestCase - setup do - @<%= singular_table_name %> = <%= fixture_name %>(:one) - end - - test "visiting the index" do - visit <%= plural_table_name %>_url - assert_selector "h1", text: "<%= class_name.pluralize.titleize %>" - end - - test "creating a <%= human_name %>" do - visit <%= plural_table_name %>_url - click_on "New <%= class_name.titleize %>" - - <%- attributes_hash.each do |attr, value| -%> - fill_in "<%= attr.humanize.titleize %>", with: <%= value %> - <%- end -%> - click_on "Create <%= human_name %>" - - assert_text "<%= human_name %> was successfully created" - click_on "Back" - end - - test "updating a <%= human_name %>" do - visit <%= plural_table_name %>_url - click_on "Edit", match: :first - - <%- attributes_hash.each do |attr, value| -%> - fill_in "<%= attr.humanize.titleize %>", with: <%= value %> - <%- end -%> - click_on "Update <%= human_name %>" - - assert_text "<%= human_name %> was successfully updated" - click_on "Back" - end - - test "destroying a <%= human_name %>" do - visit <%= plural_table_name %>_url - page.accept_confirm do - click_on "Destroy", match: :first - end - - assert_text "<%= human_name %> was successfully destroyed" - end -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt new file mode 100644 index 0000000000..f83f5a5c62 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt @@ -0,0 +1,49 @@ +require "application_system_test_case" + +<% module_namespacing do -%> +class <%= class_name.pluralize %>Test < ApplicationSystemTestCase + setup do + @<%= singular_table_name %> = <%= fixture_name %>(:one) + end + + test "visiting the index" do + visit <%= plural_table_name %>_url + assert_selector "h1", text: "<%= class_name.pluralize.titleize %>" + end + + test "creating a <%= human_name %>" do + visit <%= plural_table_name %>_url + click_on "New <%= class_name.titleize %>" + + <%- attributes_hash.each do |attr, value| -%> + fill_in "<%= attr.humanize.titleize %>", with: <%= value %> + <%- end -%> + click_on "Create <%= human_name %>" + + assert_text "<%= human_name %> was successfully created" + click_on "Back" + end + + test "updating a <%= human_name %>" do + visit <%= plural_table_name %>_url + click_on "Edit", match: :first + + <%- attributes_hash.each do |attr, value| -%> + fill_in "<%= attr.humanize.titleize %>", with: <%= value %> + <%- end -%> + click_on "Update <%= human_name %>" + + assert_text "<%= human_name %> was successfully updated" + click_on "Back" + end + + test "destroying a <%= human_name %>" do + visit <%= plural_table_name %>_url + page.accept_confirm do + click_on "Destroy", match: :first + end + + assert_text "<%= human_name %> was successfully destroyed" + end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb deleted file mode 100644 index d19212abd5..0000000000 --- a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb +++ /dev/null @@ -1,5 +0,0 @@ -require "test_helper" - -class ApplicationSystemTestCase < ActionDispatch::SystemTestCase - driven_by :selenium, using: :chrome, screen_size: [1400, 1400] -end diff --git a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt new file mode 100644 index 0000000000..d19212abd5 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :chrome, screen_size: [1400, 1400] +end diff --git a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb deleted file mode 100644 index b5ce2ba5c8..0000000000 --- a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require "application_system_test_case" - -class <%= class_name.pluralize %>Test < ApplicationSystemTestCase - # test "visiting the index" do - # visit <%= plural_table_name %>_url - # - # assert_selector "h1", text: "<%= class_name %>" - # end -end diff --git a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb.tt b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb.tt new file mode 100644 index 0000000000..b5ce2ba5c8 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb.tt @@ -0,0 +1,9 @@ +require "application_system_test_case" + +class <%= class_name.pluralize %>Test < ApplicationSystemTestCase + # test "visiting the index" do + # visit <%= plural_table_name %>_url + # + # assert_selector "h1", text: "<%= class_name %>" + # end +end diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb index cb5d8da7b1..ad2a55f496 100644 --- a/railties/test/generators/generators_test_helper.rb +++ b/railties/test/generators/generators_test_helper.rb @@ -43,9 +43,9 @@ module GeneratorsTestHelper end def copy_routes - routes = File.expand_path("../../lib/rails/generators/rails/app/templates/config/routes.rb", __dir__) + routes = File.expand_path("../../lib/rails/generators/rails/app/templates/config/routes.rb.tt", __dir__) destination = File.join(destination_root, "config") FileUtils.mkdir_p(destination) - FileUtils.cp routes, destination + FileUtils.cp routes, File.join(destination, "routes.rb") end end -- cgit v1.2.3 From 68fe6b08ee72cc47263e0d2c9ff07f75c4b42761 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Mon, 13 Nov 2017 13:24:28 -0700 Subject: Properly cast input in `update_all` The documentation claims that given values go through "normal AR type casting and serialization", which to me implies `serialize(cast(value))`, not just serialization. The docs were changed to use this wording in #22492. The tests I cited in that PR (which is the same test modified in this commit), is worded in a way that implies it should be using `cast` as well. It's possible that I originally meant "normal type casting" to imply just the call to `serialize`, but given that `update_all(archived: params['archived'])` seems to be pretty common, I'm inclined to make this change as long as no tests are broken from it. --- activerecord/CHANGELOG.md | 6 ++++++ activerecord/lib/active_record/sanitization.rb | 3 ++- activerecord/test/cases/relation_test.rb | 7 ++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c95e80755d..81ff2923ce 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* `update_all` will now pass its values to `Type#cast` before passing them to + `Type#serialize`. This means that `update_all(foo: 'true')` will properly + persist a boolean. + + *Sean Griffin* + * Add new error class `StatementTimeout` which will be raised when statement timeout exceeded. diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 1c3099f55c..90cc3373fb 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -110,7 +110,8 @@ module ActiveRecord 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) + type = type_for_attribute(attr.to_s) + value = type.serialize(type.cast(value)) "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}" end.join(", ") end diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 8362722e12..a71d8de521 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -288,13 +288,18 @@ module ActiveRecord :string end + def cast(value) + raise value unless value == "value from user" + "cast value" + end + def deserialize(value) raise value unless value == "type cast for database" "type cast from database" end def serialize(value) - raise value unless value == "value from user" + raise value unless value == "cast value" "type cast for database" end end -- cgit v1.2.3 From 704a7e425ca99af1b778c764a86e5388647631dd Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Mon, 13 Nov 2017 16:36:39 -0500 Subject: Preserve existing metadata when analyzing a blob Closes #31138. --- activestorage/app/models/active_storage/blob.rb | 2 +- activestorage/test/models/attachments_test.rb | 25 +++++++++++++++++++++++++ activestorage/test/test_helper.rb | 4 ++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 99823e14c6..2aa05d665e 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -249,7 +249,7 @@ class ActiveStorage::Blob < ActiveRecord::Base # You won't ordinarily need to call this method from a Rails application. New blobs are automatically and asynchronously # analyzed via #analyze_later when they're attached for the first time. def analyze - update! metadata: extract_metadata_via_analyzer + update! metadata: metadata.merge(extract_metadata_via_analyzer) end # Enqueues an ActiveStorage::AnalyzeJob which calls #analyze. diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index 47f2bd7911..96bc963cff 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -88,6 +88,16 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase end end + test "preserve existing metadata when analyzing a newly-attached blob" do + blob = create_file_blob(metadata: { foo: "bar" }) + + perform_enqueued_jobs do + @user.avatar.attach blob + end + + assert_equal "bar", blob.reload.metadata[:foo] + end + test "purge attached blob" do @user.avatar.attach create_blob(filename: "funky.jpg") avatar_key = @user.avatar.key @@ -193,6 +203,21 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase end end + test "preserve existing metadata when analyzing newly-attached blobs" do + blobs = [ + create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: { foo: "bar" }), + create_file_blob(filename: "video.mp4", content_type: "video/mp4", metadata: { foo: "bar" }) + ] + + perform_enqueued_jobs do + @user.highlights.attach(blobs) + end + + blobs.each do |blob| + assert_equal "bar", blob.reload.metadata[:foo] + end + end + test "purge attached blobs" do @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg") highlight_keys = @user.highlights.collect(&:key) diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb index 38408cdad3..55da781f2a 100644 --- a/activestorage/test/test_helper.rb +++ b/activestorage/test/test_helper.rb @@ -45,8 +45,8 @@ class ActiveSupport::TestCase ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type end - def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg") - ActiveStorage::Blob.create_after_upload! io: file_fixture(filename).open, filename: filename, content_type: content_type + def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: nil) + ActiveStorage::Blob.create_after_upload! io: file_fixture(filename).open, filename: filename, content_type: content_type, metadata: metadata end def create_blob_before_direct_upload(filename: "hello.txt", byte_size:, checksum:, content_type: "text/plain") -- cgit v1.2.3 From 908aaa650eadde6f732f7df6c6dcc4caaaf1c8d6 Mon Sep 17 00:00:00 2001 From: Dwight Watson Date: Tue, 14 Nov 2017 10:16:27 +1100 Subject: Sort mailer previews --- actionmailer/lib/action_mailer/preview.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb index 730ac89c94..0aea84fd2b 100644 --- a/actionmailer/lib/action_mailer/preview.rb +++ b/actionmailer/lib/action_mailer/preview.rb @@ -104,7 +104,7 @@ module ActionMailer private def load_previews if preview_path - Dir["#{preview_path}/**/*_preview.rb"].each { |file| require_dependency file } + Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file } end end -- cgit v1.2.3 From 9f8ec3535247ac41a9c92e84ddc7a3b771bc318b Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Wed, 17 May 2017 12:09:34 -0700 Subject: Built-in Redis cache store * Supports vanilla Redis, hiredis, and Redis::Distributed. * Supports Memcached-like sharding across Redises with Redis::Distributed. * Fault tolerant. If the Redis server is unavailable, no exceptions are raised. Cache fetches are treated as misses and writes are dropped. * Local cache. Hot in-memory primary cache within block/middleware scope. * `read_/write_multi` support for Redis mget/mset. Use Redis::Distributed 4.0.1+ for distributed mget support. * `delete_matched` support for Redis KEYS globs. --- activesupport/CHANGELOG.md | 38 ++ activesupport/lib/active_support/cache.rb | 45 ++- .../lib/active_support/cache/redis_cache_store.rb | 404 +++++++++++++++++++++ .../cache_increment_decrement_behavior.rb | 8 +- .../test/cache/behaviors/local_cache_behavior.rb | 6 +- .../test/cache/stores/redis_cache_store_test.rb | 151 ++++++++ guides/source/caching_with_rails.md | 47 +++ 7 files changed, 684 insertions(+), 15 deletions(-) create mode 100644 activesupport/lib/active_support/cache/redis_cache_store.rb create mode 100644 activesupport/test/cache/stores/redis_cache_store_test.rb diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 889919855c..d8059c860c 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,41 @@ +* Redis cache store. + + # Defaults to `redis://localhost:6379/0`. Only use for dev/test. + config.cache_store = :redis_cache_store + + # Supports all common cache store options (:namespace, :compress, + # :compress_threshold, :expires_in, :race_condition_tool) and all + # Redis options. + cache_password = Rails.application.secrets.redis_cache_password + config.cache_store = :redis_cache_store, driver: :hiredis, + namespace: 'myapp-cache', compress: true, timeout: 1, + url: "redis://:#{cache_password}@myapp-cache-1:6379/0" + + # Supports Redis::Distributed with multiple hosts + config.cache_store = :redis_cache_store, driver: :hiredis + namespace: 'myapp-cache', compress: true, + url: %w[ + redis://myapp-cache-1:6379/0 + redis://myapp-cache-1:6380/0 + redis://myapp-cache-2:6379/0 + redis://myapp-cache-2:6380/0 + redis://myapp-cache-3:6379/0 + redis://myapp-cache-3:6380/0 + ] + + # Or pass a builder block + config.cache_store = :redis_cache_store, + namespace: 'myapp-cache', compress: true, + redis: -> { Redis.new … } + + Deployment note: Take care to use a *dedicated Redis cache* rather + than pointing this at your existing Redis server. It won't cope well + with mixed usage patterns and it won't expire cache entries by default. + + Redis cache server setup guide: https://redis.io/topics/lru-cache + + *Jeremy Daer* + * Allow `Range#include?` on TWZ ranges In #11474 we prevented TWZ ranges being iterated over which matched diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 9f91f9b576..9848e0c623 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -12,10 +12,11 @@ require "active_support/core_ext/string/inflections" module ActiveSupport # See ActiveSupport::Cache::Store for documentation. module Cache - autoload :FileStore, "active_support/cache/file_store" - autoload :MemoryStore, "active_support/cache/memory_store" - autoload :MemCacheStore, "active_support/cache/mem_cache_store" - autoload :NullStore, "active_support/cache/null_store" + autoload :FileStore, "active_support/cache/file_store" + autoload :MemoryStore, "active_support/cache/memory_store" + autoload :MemCacheStore, "active_support/cache/mem_cache_store" + autoload :NullStore, "active_support/cache/null_store" + autoload :RedisCacheStore, "active_support/cache/redis_cache_store" # These options mean something to all cache implementations. Individual cache # implementations may support additional options. @@ -567,14 +568,34 @@ module ActiveSupport end end - # Prefixes a key with the namespace. Namespace and key will be delimited - # with a colon. - def normalize_key(key, options) - key = Cache.expand_cache_key(key) - namespace = options[:namespace] if options - prefix = namespace.is_a?(Proc) ? namespace.call : namespace - key = "#{prefix}:#{key}" if prefix - key + # Expands and namespaces the cache key. May be overridden by + # cache stores to do additional normalization. + def normalize_key(key, options = nil) + namespace_key Cache.expand_cache_key(key), options + end + + # Prefix the key with a namespace string: + # + # namespace_key 'foo', namespace: 'cache' + # # => 'cache:foo' + # + # With a namespace block: + # + # namespace_key 'foo', namespace: -> { 'cache' } + # # => 'cache:foo' + def namespace_key(key, options = nil) + options = merged_options(options) + namespace = options[:namespace] + + if namespace.respond_to?(:call) + namespace = namespace.call + end + + if namespace + "#{namespace}:#{key}" + else + key + end end def normalize_version(key, options = nil) diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb new file mode 100644 index 0000000000..358de9203d --- /dev/null +++ b/activesupport/lib/active_support/cache/redis_cache_store.rb @@ -0,0 +1,404 @@ +# frozen_string_literal: true + +begin + gem "redis", ">= 4.0.1" + require "redis" + require "redis/distributed" +rescue LoadError + warn "The Redis cache store requires the redis gem, version 4.0.1 or later. Please add it to your Gemfile: `gem \"redis\", \"~> 4.0\"`" + raise +end + +# Prefer the hiredis driver but don't require it. +begin + require "redis/connection/hiredis" +rescue LoadError +end + +require "digest/sha2" +require "active_support/core_ext/marshal" + +module ActiveSupport + module Cache + # Redis cache store. + # + # Deployment note: Take care to use a *dedicated Redis cache* rather + # than pointing this at your existing Redis server. It won't cope well + # with mixed usage patterns and it won't expire cache entries by default. + # + # Redis cache server setup guide: https://redis.io/topics/lru-cache + # + # * Supports vanilla Redis, hiredis, and Redis::Distributed. + # * Supports Memcached-like sharding across Redises with Redis::Distributed. + # * Fault tolerant. If the Redis server is unavailable, no exceptions are + # raised. Cache fetches are all misses and writes are dropped. + # * Local cache. Hot in-memory primary cache within block/middleware scope. + # * `read_/write_multi` support for Redis mget/mset. Use Redis::Distributed + # 4.0.1+ for distributed mget support. + # * `delete_matched` support for Redis KEYS globs. + class RedisCacheStore < Store + # Keys are truncated with their own SHA2 digest if they exceed 1kB + MAX_KEY_BYTESIZE = 1024 + + DEFAULT_REDIS_OPTIONS = { + connect_timeout: 20, + read_timeout: 1, + write_timeout: 1, + reconnect_attempts: 0, + } + + DEFAULT_ERROR_HANDLER = -> (method:, returning:, exception:) { + logger.error { "RedisCacheStore: #{method} failed, returned #{returning.inspect}: #{e.class}: #{e.message}" } if logger + } + + DELETE_GLOB_LUA = "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" + private_constant :DELETE_GLOB_LUA + + # Support raw values in the local cache strategy. + module LocalCacheWithRaw # :nodoc: + private + def read_entry(key, options) + entry = super + if options[:raw] && local_cache && entry + entry = deserialize_entry(entry.value) + end + entry + end + + 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 + super(key, raw_entry, options) + else + super + end + end + + def write_multi_entries(entries, options) + if options[:raw] && local_cache + raw_entries = entries.map do |key, entry| + raw_entry = Entry.new(entry.value.to_s) + raw_entry.expires_at = entry.expires_at + end.to_h + + super(raw_entries, options) + else + super + end + end + end + + prepend Strategy::LocalCache + prepend LocalCacheWithRaw + + class << self + # Factory method to create a new Redis instance. + # + # Handles four options: :redis block, :redis instance, single :url + # string, and multiple :url strings. + # + # Option Class Result + # :redis Proc -> options[:redis].call + # :redis Object -> options[:redis] + # :url String -> Redis.new(url: …) + # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …]) + # + def build_redis(redis: nil, url: nil, **redis_options) #:nodoc: + urls = Array(url) + + if redis.respond_to?(:call) + redis.call + elsif redis + redis + elsif urls.size > 1 + build_redis_distributed_client urls: urls, **redis_options + else + build_redis_client url: urls.first, **redis_options + end + end + + private + def build_redis_distributed_client(urls:, **redis_options) + ::Redis::Distributed.new([], DEFAULT_REDIS_OPTIONS.merge(redis_options)).tap do |dist| + urls.each { |u| dist.add_node url: u } + end + end + + def build_redis_client(url:, **redis_options) + ::Redis.new DEFAULT_REDIS_OPTIONS.merge(redis_options.merge(url: url)) + end + end + + attr_reader :redis_options + attr_reader :max_key_bytesize + + # Creates a new Redis cache store. + # + # Handles three options: block provided to instantiate, single URL + # provided, and multiple URLs provided. + # + # :redis Proc -> options[:redis].call + # :url String -> Redis.new(url: …) + # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …]) + # + # No namespace is set by default. Provide one if the Redis cache + # server is shared with other apps: `namespace: 'myapp-cache'`. + # + # Compression is enabled by default with a 1kB threshold, so cached + # values larger than 1kB are automatically compressed. Disable by + # passing `cache: false` or change the threshold by passing + # `compress_threshold: 4.kilobytes`. + # + # No expiry is set on cache entries by default. Redis is expected to + # be configured with an eviction policy that automatically deletes + # least-recently or -frequently used keys when it reaches max memory. + # See https://redis.io/topics/lru-cache for cache server setup. + # + # Race condition TTL is not set by default. This can be used to avoid + # "thundering herd" cache writes when hot cache entries are expired. + # See ActiveSupport::Cache::Store#fetch for more. + def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options) + @redis_options = redis_options + + @max_key_bytesize = MAX_KEY_BYTESIZE + @error_handler = error_handler + + super namespace: namespace, + compress: compress, compress_threshold: compress_threshold, + expires_in: expires_in, race_condition_ttl: race_condition_ttl + end + + def redis + @redis ||= self.class.build_redis(**redis_options) + end + + def inspect + instance = @redis || @redis_options + "<##{self.class} options=#{options.inspect} redis=#{instance.inspect}>" + end + + # Cache Store API implementation. + # + # Read multiple values at once. Returns a hash of requested keys -> + # fetched values. + def read_multi(*names) + if mget_capable? + read_multi_mget(*names) + else + super + end + end + + # Cache Store API implementation. + # + # Supports Redis KEYS glob patterns: + # + # h?llo matches hello, hallo and hxllo + # h*llo matches hllo and heeeello + # h[ae]llo matches hello and hallo, but not hillo + # h[^e]llo matches hallo, hbllo, ... but not hello + # h[a-b]llo matches hallo and hbllo + # + # Use \ to escape special characters if you want to match them verbatim. + # + # See https://redis.io/commands/KEYS for more. + # + # Failsafe: Raises errors. + def delete_matched(matcher, options = nil) + instrument :delete_matched, matcher do + case matcher + when String + redis.eval DELETE_GLOB_LUA, [], [namespace_key(matcher, options)] + else + raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}" + end + end + end + + # Cache Store API implementation. + # + # Increment a cached value. This method uses the Redis incr atomic + # operator and can only be used on values written with the :raw option. + # Calling it on a value not stored with :raw will initialize that value + # to zero. + # + # Failsafe: Raises errors. + def increment(name, amount = 1, options = nil) + instrument :increment, name, amount: amount do + redis.incrby normalize_key(name, options), amount + end + end + + # Cache Store API implementation. + # + # Decrement a cached value. This method uses the Redis decr atomic + # operator and can only be used on values written with the :raw option. + # Calling it on a value not stored with :raw will initialize that value + # to zero. + # + # Failsafe: Raises errors. + def decrement(name, amount = 1, options = nil) + instrument :decrement, name, amount: amount do + redis.decrby normalize_key(name, options), amount + end + end + + # Cache Store API implementation. + # + # Removes expired entries. Handled natively by Redis least-recently-/ + # least-frequently-used expiry, so manual cleanup is not supported. + def cleanup(options = nil) + super + end + + # Clear the entire cache on all Redis servers. Safe to use on + # shared servers if the cache is namespaced. + # + # Failsafe: Raises errors. + def clear(options = nil) + failsafe :clear do + if namespace = merged_options(options)[namespace] + delete_matched "*", namespace: namespace + else + redis.flushdb + end + end + end + + def mget_capable? #:nodoc: + set_redis_capabilities unless defined? @mget_capable + @mget_capable + end + + def mset_capable? #:nodoc: + set_redis_capabilities unless defined? @mset_capable + @mset_capable + end + + private + def set_redis_capabilities + case redis + when Redis::Distributed + @mget_capable = true + @mset_capable = false + else + @mget_capable = true + @mset_capable = true + end + end + + # Store provider interface: + # Read an entry from the cache. + def read_entry(key, options = nil) + failsafe :read_entry do + deserialize_entry redis.get(key) + end + end + + def read_multi_mget(*names) + options = names.extract_options! + options = merged_options(options) + + keys_to_names = names.map { |name| [ normalize_key(name, options), name ] }.to_h + values = redis.mget(*keys_to_names.keys) + + keys_to_names.zip(values).each_with_object({}) do |((key, name), value), results| + if value + entry = deserialize_entry(value) + unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options)) + results[name] = entry.value + end + end + end + end + + # Write an entry to the cache. + # + # Requires Redis 2.6.12+ for extended SET options. + def write_entry(key, entry, unless_exist: false, raw: false, expires_in: nil, race_condition_ttl: nil, **options) + value = raw ? entry.value.to_s : serialize_entry(entry) + + # If race condition TTL is in use, ensure that cache entries + # stick around a bit longer after they would have expired + # so we can purposefully serve stale entries. + if race_condition_ttl && expires_in && expires_in > 0 && !raw + expires_in += 5.minutes + end + + failsafe :write_entry do + if unless_exist || expires_in + modifiers = {} + modifiers[:nx] = unless_exist + modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in + + redis.set key, value, modifiers + else + redis.set key, value + end + end + end + + # Delete an entry from the cache. + def delete_entry(key, options) + failsafe :delete_entry, returning: false do + redis.del key + end + end + + # Nonstandard store provider API to write multiple values at once. + def write_multi_entries(entries, expires_in: nil, **options) + if entries.any? + if mset_capable? && expires_in.nil? + failsafe :write_multi_entries do + redis.mapped_mset(entries) + end + else + super + end + end + end + + # Truncate keys that exceed 1kB. + def normalize_key(key, options) + truncate_key super + end + + def truncate_key(key) + if key.bytesize > max_key_bytesize + suffix = ":sha2:#{Digest::SHA2.hexdigest(key)}" + truncate_at = max_key_bytesize - suffix.bytesize + "#{key.byteslice(0, truncate_at)}#{suffix}" + else + key + end + end + + def deserialize_entry(raw_value) + if raw_value + entry = Marshal.load(raw_value) rescue raw_value + entry.is_a?(Entry) ? entry : Entry.new(entry) + end + end + + def serialize_entry(entry) + Marshal.dump(entry) + end + + def failsafe(method, returning: nil) + yield + rescue ::Redis::BaseConnectionError => e + handle_exception exception: e, method: method, returning: returning + returning + end + + def handle_exception(exception:, method:, returning:) + if @error_handler + @error_handler.(method: method, exception: exception, returning: returning) + end + rescue => failsafe + warn "RedisCacheStore ignored exception in handle_exception: #{failsafe.class}: #{failsafe.message}\n #{failsafe.backtrace.join("\n ")}" + end + end + end +end diff --git a/activesupport/test/cache/behaviors/cache_increment_decrement_behavior.rb b/activesupport/test/cache/behaviors/cache_increment_decrement_behavior.rb index 2fa2d7af88..16b7abc679 100644 --- a/activesupport/test/cache/behaviors/cache_increment_decrement_behavior.rb +++ b/activesupport/test/cache/behaviors/cache_increment_decrement_behavior.rb @@ -8,7 +8,9 @@ module CacheIncrementDecrementBehavior assert_equal 2, @cache.read("foo").to_i assert_equal 3, @cache.increment("foo") assert_equal 3, @cache.read("foo").to_i - assert_nil @cache.increment("bar") + + missing = @cache.increment("bar") + assert(missing.nil? || missing == 1) end def test_decrement @@ -18,6 +20,8 @@ module CacheIncrementDecrementBehavior assert_equal 2, @cache.read("foo").to_i assert_equal 1, @cache.decrement("foo") assert_equal 1, @cache.read("foo").to_i - assert_nil @cache.decrement("bar") + + missing = @cache.decrement("bar") + assert(missing.nil? || missing == -1) end end diff --git a/activesupport/test/cache/behaviors/local_cache_behavior.rb b/activesupport/test/cache/behaviors/local_cache_behavior.rb index 8dec8090b1..f7302df4c8 100644 --- a/activesupport/test/cache/behaviors/local_cache_behavior.rb +++ b/activesupport/test/cache/behaviors/local_cache_behavior.rb @@ -20,7 +20,11 @@ module LocalCacheBehavior end def test_cleanup_clears_local_cache_but_not_remote_cache - skip unless @cache.class.instance_methods(false).include?(:cleanup) + begin + @cache.cleanup + rescue NotImplementedError + skip + end @cache.with_local_cache do @cache.write("foo", "bar") diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb new file mode 100644 index 0000000000..988de9207f --- /dev/null +++ b/activesupport/test/cache/stores/redis_cache_store_test.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "active_support/cache" +require "active_support/cache/redis_cache_store" +require_relative "../behaviors" + +module ActiveSupport::Cache::RedisCacheStoreTests + class LookupTest < ActiveSupport::TestCase + test "may be looked up as :redis_cache_store" do + assert_kind_of ActiveSupport::Cache::RedisCacheStore, + ActiveSupport::Cache.lookup_store(:redis_cache_store) + end + end + + class InitializationTest < ActiveSupport::TestCase + test "omitted URL uses Redis client with default settings" do + assert_called_with Redis, :new, [ + url: nil, + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0, + ] do + build + end + end + + test "no URLs uses Redis client with default settings" do + assert_called_with Redis, :new, [ + url: nil, + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0, + ] do + build url: [] + end + end + + test "singular URL uses Redis client" do + assert_called_with Redis, :new, [ + url: "redis://localhost:6379/0", + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0, + ] do + build url: "redis://localhost:6379/0" + end + end + + test "one URL uses Redis client" do + assert_called_with Redis, :new, [ + url: "redis://localhost:6379/0", + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0, + ] do + build url: %w[ redis://localhost:6379/0 ] + end + end + + test "multiple URLs uses Redis::Distributed client" do + assert_called_with Redis, :new, [ + [ url: "redis://localhost:6379/0", + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0 ], + [ url: "redis://localhost:6379/1", + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0 ], + ], returns: Redis.new do + @cache = build url: %w[ redis://localhost:6379/0 redis://localhost:6379/1 ] + assert_kind_of ::Redis::Distributed, @cache.redis + end + end + + test "block argument uses yielded client" do + block = -> { :custom_redis_client } + assert_called block, :call do + build redis: block + end + end + + private + def build(**kwargs) + ActiveSupport::Cache::RedisCacheStore.new(**kwargs).tap do |cache| + cache.redis + end + end + end + + class StoreTest < ActiveSupport::TestCase + setup do + @namespace = "namespace" + + @cache = ActiveSupport::Cache::RedisCacheStore.new(timeout: 0.1, namespace: @namespace, expires_in: 60) + #@cache.logger = Logger.new($stdout) # For test debugging + + # For LocalCacheBehavior tests + @peek = ActiveSupport::Cache::RedisCacheStore.new(timeout: 0.1, namespace: @namespace) + end + + teardown do + @cache.clear + @cache.redis.disconnect! + end + end + + class RedisCacheStoreCommonBehaviorTest < StoreTest + include CacheStoreBehavior + include CacheStoreVersionBehavior + include LocalCacheBehavior + include CacheIncrementDecrementBehavior + include AutoloadingCacheBehavior + end + + # Separate test class so we can omit the namespace which causes expected, + # appropriate complaints about incompatible string encodings. + class KeyEncodingSafetyTest < StoreTest + include EncodedKeyCacheBehavior + + setup do + @cache = ActiveSupport::Cache::RedisCacheStore.new(timeout: 0.1) + @cache.logger = nil + end + end + + class StoreAPITest < StoreTest + end + + class FailureSafetyTest < StoreTest + test "fetch read failure returns nil" do + end + + test "fetch read failure does not attempt to write" do + end + + test "write failure returns nil" do + end + end + + class DeleteMatchedTest < StoreTest + test "deletes keys matching glob" do + @cache.write("foo", "bar") + @cache.write("fu", "baz") + @cache.delete_matched("foo*") + assert !@cache.exist?("foo") + assert @cache.exist?("fu") + end + + test "fails with regexp matchers" do + assert_raise ArgumentError do + @cache.delete_matched(/OO/i) + end + end + end +end diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 96650b5be9..c2173d0d9e 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -444,6 +444,53 @@ The `write` and `fetch` methods on this cache accept two additional options that config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com" ``` +### ActiveSupport::Cache::RedisCacheStore + +The Redis cache store takes advantage of Redis support for least-recently-used +and least-frequently-used key eviction when it reaches max memory, allowing it +to behave much like a Memcached cache server. + +Deployment note: Redis doesn't expire keys by default, so take care to use a +dedicated Redis cache server. Don't fill up your persistent-Redis server with +volatile cache data! Read the +[Redis cache server setup guide](https://redis.io/topics/lru-cache) in detail. + +For an all-cache Redis server, set `maxmemory-policy` to an `allkeys` policy. +Redis 4+ support least-frequently-used (`allkeys-lfu`) eviction, an excellent +default choice. Redis 3 and earlier should use `allkeys-lru` for +least-recently-used eviction. + +Set cache read and write timeouts relatively low. Regenerating a cached value +is often faster than waiting more than a second to retrieve it. Both read and +write timeouts default to 1 second, but may be set lower if your network is +consistently low latency. + +Cache reads and writes never raise exceptions. They just return `nil` instead, +behaving as if there was nothing in the cache. To gauge whether your cache is +hitting exceptions, you may provide an `error_handler` to report to an +exception gathering service. It must accept three keyword arguments: `method`, +the cache store method that was originally called; `returning`, the value that +was returned to the user, typically `nil`; and `exception`, the exception that +was rescued. + +Putting it all together, a production Redis cache store may look something +like this: + +```ruby +cache_servers = %w[ "redis://cache-01:6379/0", "redis://cache-02:6379/0", … ], +config.cache_store = :redis_cache_store, url: cache_servers, + + connect_timeout: 30, # Defaults to 20 seconds + read_timeout: 0.2, # Defaults to 1 second + write_timeout: 0.2, # Defaults to 1 second + + error_handler: -> (method:, returning:, exception:) { + # Report errors to Sentry as warnings + Raven.capture_exception exception, level: 'warning", + tags: { method: method, returning: returning } + } +``` + ### ActiveSupport::Cache::NullStore This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache` but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss. -- cgit v1.2.3 From b6d5e46311d7ea59539c1f45c6ffb269eeb23912 Mon Sep 17 00:00:00 2001 From: Yuji Yaginuma Date: Tue, 14 Nov 2017 13:54:58 +0900 Subject: Add `environment` as dependency of `load_config` (#31135) Currently the environment is not loaded in some db tasks. Therefore, if use encrypted secrets values in `database.yml`, `read_encrypted_secrets` will not be true, so the value can not be used correctly. To fix this, added `environment` as dependency of `load_config`. It also removes explicit `environment` dependencies that are no longer needed. Fixes #30717 --- .../lib/active_record/railties/databases.rake | 48 +++++++++++----------- railties/test/application/rake/dbs_test.rb | 20 ++++++++- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 3bca2982e0..fce3e1c5cf 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -4,16 +4,16 @@ require "active_record" db_namespace = namespace :db do desc "Set the environment value for the database" - task "environment:set" => [:environment, :load_config] do + task "environment:set" => :load_config do ActiveRecord::InternalMetadata.create_table ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment end - task check_protected_environments: [:environment, :load_config] do + task check_protected_environments: :load_config do ActiveRecord::Tasks::DatabaseTasks.check_protected_environments! end - task :load_config do + task load_config: :environment do ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {} ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths end @@ -56,7 +56,7 @@ db_namespace = namespace :db do end desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)." - task migrate: [:environment, :load_config] do + task migrate: :load_config do ActiveRecord::Tasks::DatabaseTasks.migrate db_namespace["_dump"].invoke end @@ -78,7 +78,7 @@ db_namespace = namespace :db do namespace :migrate do # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).' - task redo: [:environment, :load_config] do + task redo: :load_config do raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty? if ENV["VERSION"] @@ -94,7 +94,7 @@ db_namespace = namespace :db do task reset: ["db:drop", "db:create", "db:migrate"] # desc 'Runs the "up" for a given migration VERSION.' - task up: [:environment, :load_config] do + task up: :load_config do raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty? ActiveRecord::Tasks::DatabaseTasks.check_target_version @@ -108,7 +108,7 @@ db_namespace = namespace :db do end # desc 'Runs the "down" for a given migration VERSION.' - task down: [:environment, :load_config] do + task down: :load_config do raise "VERSION is required - To go down one migration, use db:rollback" if !ENV["VERSION"] || ENV["VERSION"].empty? ActiveRecord::Tasks::DatabaseTasks.check_target_version @@ -122,7 +122,7 @@ db_namespace = namespace :db do end desc "Display status of migrations" - task status: [:environment, :load_config] do + task status: :load_config do unless ActiveRecord::SchemaMigration.table_exists? abort "Schema migrations table does not exist yet." end @@ -140,14 +140,14 @@ db_namespace = namespace :db do end desc "Rolls the schema back to the previous version (specify steps w/ STEP=n)." - task rollback: [:environment, :load_config] do + task rollback: :load_config do step = ENV["STEP"] ? ENV["STEP"].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Tasks::DatabaseTasks.migrations_paths, step) db_namespace["_dump"].invoke end # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' - task forward: [:environment, :load_config] do + task forward: :load_config do step = ENV["STEP"] ? ENV["STEP"].to_i : 1 ActiveRecord::Migrator.forward(ActiveRecord::Tasks::DatabaseTasks.migrations_paths, step) db_namespace["_dump"].invoke @@ -157,12 +157,12 @@ db_namespace = namespace :db do task reset: [ "db:drop", "db:setup" ] # desc "Retrieves the charset for the current environment's database" - task charset: [:environment, :load_config] do + task charset: :load_config do puts ActiveRecord::Tasks::DatabaseTasks.charset_current end # desc "Retrieves the collation for the current environment's database" - task collation: [:environment, :load_config] do + task collation: :load_config do begin puts ActiveRecord::Tasks::DatabaseTasks.collation_current rescue NoMethodError @@ -171,12 +171,12 @@ db_namespace = namespace :db do end desc "Retrieves the current schema version number" - task version: [:environment, :load_config] do + task version: :load_config do puts "Current version: #{ActiveRecord::Migrator.current_version}" end # desc "Raises an error if there are pending migrations" - task abort_if_pending_migrations: [:environment, :load_config] do + task abort_if_pending_migrations: :load_config do pending_migrations = ActiveRecord::Migrator.open(ActiveRecord::Tasks::DatabaseTasks.migrations_paths).pending_migrations if pending_migrations.any? @@ -199,7 +199,7 @@ db_namespace = namespace :db do namespace :fixtures do desc "Loads fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." - task load: [:environment, :load_config] do + task load: :load_config do require "active_record/fixtures" base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path @@ -221,7 +221,7 @@ db_namespace = namespace :db do end # desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." - task identify: [:environment, :load_config] do + task identify: :load_config do require "active_record/fixtures" label, id = ENV["LABEL"], ENV["ID"] @@ -247,7 +247,7 @@ db_namespace = namespace :db do namespace :schema do desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record" - task dump: [:environment, :load_config] do + task dump: :load_config do require "active_record/schema_dumper" filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema.rb") File.open(filename, "w:utf-8") do |file| @@ -257,7 +257,7 @@ db_namespace = namespace :db do end desc "Loads a schema.rb file into the database" - task load: [:environment, :load_config, :check_protected_environments] do + task load: [:load_config, :check_protected_environments] do ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV["SCHEMA"]) end @@ -267,14 +267,14 @@ db_namespace = namespace :db do namespace :cache do desc "Creates a db/schema_cache.yml file." - task dump: [:environment, :load_config] do + task dump: :load_config do conn = ActiveRecord::Base.connection filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml") ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(conn, filename) end desc "Clears a db/schema_cache.yml file." - task clear: [:environment, :load_config] do + task clear: :load_config do filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml") rm_f filename, verbose: false end @@ -284,7 +284,7 @@ db_namespace = namespace :db do namespace :structure do desc "Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql" - task dump: [:environment, :load_config] do + task dump: :load_config do filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "structure.sql") current_config = ActiveRecord::Tasks::DatabaseTasks.current_config ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename) @@ -299,7 +299,7 @@ db_namespace = namespace :db do end desc "Recreates the databases from the structure.sql file" - task load: [:environment, :load_config, :check_protected_environments] do + task load: [:load_config, :check_protected_environments] do ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV["SCHEMA"]) end @@ -338,12 +338,12 @@ db_namespace = namespace :db do end # desc "Empty the test database" - task purge: %w(environment load_config check_protected_environments) do + task purge: %w(load_config check_protected_environments) do ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations["test"] end # desc 'Load the test schema' - task prepare: %w(environment load_config) do + task prepare: :load_config do unless ActiveRecord::Base.configurations.blank? db_namespace["test:load"].invoke end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index fd22477539..0235210fdd 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -26,12 +26,12 @@ module ApplicationTests FileUtils.rm_rf("#{app_path}/config/database.yml") end - def db_create_and_drop(expected_database) + def db_create_and_drop(expected_database, environment_loaded: true) Dir.chdir(app_path) do output = rails("db:create") assert_match(/Created database/, output) assert File.exist?(expected_database) - assert_equal expected_database, ActiveRecord::Base.connection_config[:database] + assert_equal expected_database, ActiveRecord::Base.connection_config[:database] if environment_loaded output = rails("db:drop") assert_match(/Dropped database/, output) assert !File.exist?(expected_database) @@ -49,6 +49,22 @@ module ApplicationTests db_create_and_drop database_url_db_name end + test "db:create and db:drop respect environment setting" do + app_file "config/database.yml", <<-YAML + development: + database: <%= Rails.application.config.database %> + adapter: sqlite3 + YAML + + app_file "config/environments/development.rb", <<-RUBY + Rails.application.configure do + config.database = "db/development.sqlite3" + end + RUBY + + db_create_and_drop "db/development.sqlite3", environment_loaded: false + end + def with_database_existing Dir.chdir(app_path) do set_database_url -- cgit v1.2.3 From ed100166874fb4a542c5aaba933a4cca5ed72269 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Mon, 13 Nov 2017 19:16:53 -0700 Subject: Cache: Enable compression by default for values > 1kB. Compression has long been available, but opt-in and at a 16kB threshold. It wasn't enabled by default due to CPU cost. Today it's cheap and typical cache data is eminently compressible, such as HTML or JSON fragments. Compression dramatically reduces Memcached/Redis mem usage, which means the same cache servers can store more data, which means higher hit rates. To disable compression, pass `compress: false` to the initializer. --- activesupport/CHANGELOG.md | 12 ++++++++++++ activesupport/lib/active_support/cache.rb | 20 +++++++++----------- .../test/cache/behaviors/cache_store_behavior.rb | 10 ++++++++++ activesupport/test/cache/cache_entry_test.rb | 13 ++++++++++--- guides/source/caching_with_rails.md | 4 ++-- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index d8059c860c..24c7f362e9 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -36,6 +36,18 @@ *Jeremy Daer* +* Cache: Enable compression by default for values > 1kB. + + Compression has long been available, but opt-in and at a 16kB threshold. + It wasn't enabled by default due to CPU cost. Today it's cheap and typical + cache data is eminently compressible, such as HTML or JSON fragments. + Compression dramatically reduces Memcached/Redis mem usage, which means + the same cache servers can store more data, which means higher hit rates. + + To disable compression, pass `compress: false` to the initializer. + + *Jeremy Daer* + * Allow `Range#include?` on TWZ ranges In #11474 we prevented TWZ ranges being iterated over which matched diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 9848e0c623..395aa5e8f1 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -154,12 +154,11 @@ module ActiveSupport # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace # - # Caches can also store values in a compressed format to save space and - # reduce time spent sending data. Since there is overhead, values must be - # large enough to warrant compression. To turn on compression either pass - # compress: true in the initializer or as an option to +fetch+ - # or +write+. To specify the threshold at which to compress values, set the - # :compress_threshold option. The default threshold is 16K. + # Cached data larger than 1kB are compressed by default. To turn off + # compression, pass compress: false to the initializer or to + # individual +fetch+ or +write+ method calls. The 1kB compression + # threshold is configurable with the :compress_threshold option, + # specified in bytes. class Store cattr_accessor :logger, instance_writer: true @@ -218,8 +217,7 @@ module ActiveSupport # ask whether you should force a cache write. Otherwise, it's clearer to # just call Cache#write. # - # Setting :compress will store a large cache entry set by the call - # in a compressed format. + # Setting compress: false disables compression of the cache entry. # # Setting :expires_in will set an expiration time on the cache. # All caches support auto-expiring content after a specified number of @@ -664,7 +662,7 @@ module ActiveSupport class Entry # :nodoc: attr_reader :version - DEFAULT_COMPRESS_LIMIT = 16.kilobytes + DEFAULT_COMPRESS_LIMIT = 1.kilobyte # Creates a new cache entry for the specified value. Options supported are # +:compress+, +:compress_threshold+, and +:expires_in+. @@ -739,8 +737,8 @@ module ActiveSupport private def should_compress?(value, options) - if value && options[:compress] - compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT + if value && options.fetch(:compress, true) + compress_threshold = options.fetch(:compress_threshold, DEFAULT_COMPRESS_LIMIT) serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize return true if serialized_value_size >= compress_threshold diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb index 582e902f72..73a9b2a71c 100644 --- a/activesupport/test/cache/behaviors/cache_store_behavior.rb +++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb @@ -146,6 +146,16 @@ module CacheStoreBehavior assert_nil @cache.read("foo") end + def test_read_and_write_uncompressed_small_data + @cache.write("foo", "bar", compress: false) + assert_equal "bar", @cache.read("foo") + end + + def test_read_and_write_uncompressed_nil + @cache.write("foo", nil, compress: false) + assert_nil @cache.read("foo") + end + def test_cache_key obj = Object.new def obj.cache_key diff --git a/activesupport/test/cache/cache_entry_test.rb b/activesupport/test/cache/cache_entry_test.rb index 51b214ad8f..80ff7ad564 100644 --- a/activesupport/test/cache/cache_entry_test.rb +++ b/activesupport/test/cache/cache_entry_test.rb @@ -14,16 +14,23 @@ class CacheEntryTest < ActiveSupport::TestCase end end - def test_compress_values + def test_compressed_values value = "value" * 100 entry = ActiveSupport::Cache::Entry.new(value, compress: true, compress_threshold: 1) assert_equal value, entry.value assert(value.bytesize > entry.size, "value is compressed") end - def test_non_compress_values + def test_compressed_by_default value = "value" * 100 - entry = ActiveSupport::Cache::Entry.new(value) + entry = ActiveSupport::Cache::Entry.new(value, compress_threshold: 1) + assert_equal value, entry.value + assert(value.bytesize > entry.size, "value is compressed") + end + + def test_uncompressed_values + value = "value" * 100 + entry = ActiveSupport::Cache::Entry.new(value, compress: false) assert_equal value, entry.value assert_equal value.bytesize, entry.size end diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index c2173d0d9e..31bc478015 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -366,9 +366,9 @@ There are some common options used by all cache implementations. These can be pa * `:namespace` - This option can be used to create a namespace within the cache store. It is especially useful if your application shares a cache with other applications. -* `:compress` - This option can be used to indicate that compression should be used in the cache. This can be useful for transferring large cache entries over a slow network. +* `:compress` - Enabled by default. Compresses cache entries so more data can be stored in the same memory footprint, leading to fewer cache evictions and higher hit rates. -* `:compress_threshold` - This option is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes. +* `:compress_threshold` - Defaults to 1kB. Cache entries larger than this threshold, specified in bytes, are compressed. * `:expires_in` - This option sets an expiration time in seconds for the cache entry when it will be automatically removed from the cache. -- cgit v1.2.3 From 5345b61f29568c80bee6f8d0000a61e35d93429a Mon Sep 17 00:00:00 2001 From: Nikita Penzin Date: Sun, 30 Jul 2017 18:24:20 +0300 Subject: Ensure plugin_generator adds to new line in Gemfile Ensure plugin_generator adds to new line in Gemfile, even if the Gemfile does not end with an empty line. [Lisa Ugray, Nikita Penzin] --- railties/lib/rails/generators/rails/plugin/plugin_generator.rb | 2 +- railties/test/generators/plugin_generator_test.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index a1209e4624..e946452bd6 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -167,7 +167,7 @@ task default: :test gemfile_in_app_path = File.join(rails_app_path, "Gemfile") if File.exist? gemfile_in_app_path - entry = "gem '#{name}', path: '#{relative_path}'" + entry = "\ngem '#{name}', path: '#{relative_path}'" append_file gemfile_in_app_path, entry end end diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index e8372dea47..c7c82de12b 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -530,10 +530,11 @@ class PluginGeneratorTest < Rails::Generators::TestCase gemfile_path = "#{Rails.root}/Gemfile" Object.const_set("APP_PATH", Rails.root) FileUtils.touch gemfile_path + File.write(gemfile_path, "#foo") run_generator - assert_file gemfile_path, /gem 'bukkits', path: 'tmp\/bukkits'/ + assert_file gemfile_path, /^gem 'bukkits', path: 'tmp\/bukkits'/ ensure Object.send(:remove_const, "APP_PATH") FileUtils.rm gemfile_path -- cgit v1.2.3 From 0bcfa999b8deb7ea5d15dab9597bc637d226b565 Mon Sep 17 00:00:00 2001 From: Rob Date: Tue, 14 Nov 2017 13:26:01 +0000 Subject: Update layouts_and_rendering.md [ci skip] --- guides/source/layouts_and_rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index b9b327252f..f4597b0e60 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -1266,7 +1266,7 @@ You can also pass in arbitrary local variables to any partial you are rendering In this case, the partial will have access to a local variable `title` with the value "Products Page". -TIP: Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by `_counter`. For example, if you're rendering `@products`, within the partial you can refer to `product_counter` to tell you how many times the partial has been rendered. This does not work in conjunction with the `as: :value` option. +TIP: Rails also makes a counter variable available within a partial called by the collection, named after the title of the partial followed by `_counter`. For example, when rendering a collection `@products` the partial `_product.html.erb` can access the variable `product_counter` which indexes the number of times it has been rendered within the enclosing view. You can also specify a second partial to be rendered between instances of the main partial by using the `:spacer_template` option: -- cgit v1.2.3 From 67dbfc69f690f231d5b8257e03b8338af19c1d05 Mon Sep 17 00:00:00 2001 From: Nikolai B Date: Tue, 14 Nov 2017 14:12:40 +0000 Subject: Update `exists?` documentation Make it clear that `exists?` can be chained onto a relation --- activerecord/lib/active_record/relation/finder_methods.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 18566b5662..706fd57704 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -284,7 +284,7 @@ module ActiveRecord # * Hash - Finds the record that matches these +find+-style conditions # (such as {name: 'David'}). # * +false+ - Returns always +false+. - # * No args - Returns +false+ if the table is empty, +true+ otherwise. + # * No args - Returns +false+ if the relation is empty, +true+ otherwise. # # For more information about specifying conditions as a hash or array, # see the Conditions section in the introduction to ActiveRecord::Base. @@ -300,6 +300,7 @@ module ActiveRecord # Person.exists?(name: 'David') # Person.exists?(false) # Person.exists? + # Person.where(name: 'Spartacus', rating: 4).exists? def exists?(conditions = :none) if Base === conditions raise ArgumentError, <<-MSG.squish -- cgit v1.2.3 From 499a4164ce9816c4913bf3db14787ea99ef2c266 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Tue, 14 Nov 2017 10:42:10 -0500 Subject: Introduce ActiveStorage::Attached::{One,Many}#detach --- activestorage/lib/active_storage/attached/many.rb | 6 +++++- activestorage/lib/active_storage/attached/one.rb | 15 +++++++------ activestorage/test/models/attachments_test.rb | 26 +++++++++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/activestorage/lib/active_storage/attached/many.rb b/activestorage/lib/active_storage/attached/many.rb index 1e0657c33c..0b3400bccf 100644 --- a/activestorage/lib/active_storage/attached/many.rb +++ b/activestorage/lib/active_storage/attached/many.rb @@ -13,7 +13,6 @@ module ActiveStorage end # Associates one or several attachments with the current record, saving them to the database. - # Examples: # # document.images.attach(params[:images]) # Array of ActionDispatch::Http::UploadedFile objects # document.images.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload @@ -36,6 +35,11 @@ module ActiveStorage attachments.any? end + # Deletes associated attachments without purging them, leaving their respective blobs in place. + def detach + attachments.destroy_all if attached? + end + # Directly purges each associated attachment (i.e. destroys the blobs and # attachments and deletes the files on the service). def purge diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb index dc19512484..7092f6b109 100644 --- a/activestorage/lib/active_storage/attached/one.rb +++ b/activestorage/lib/active_storage/attached/one.rb @@ -14,7 +14,6 @@ module ActiveStorage end # Associates a given attachment with the current record, saving it to the database. - # Examples: # # person.avatar.attach(params[:avatar]) # ActionDispatch::Http::UploadedFile object # person.avatar.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload @@ -39,6 +38,14 @@ module ActiveStorage attachment.present? end + # Deletes the attachment without purging it, leaving its blob in place. + def detach + if attached? + attachment.destroy + write_attachment nil + end + end + # Directly purges the attachment (i.e. destroys the blob and # attachment and deletes the file on the service). def purge @@ -59,16 +66,12 @@ module ActiveStorage def replace(attachable) blob.tap do transaction do - destroy_attachment + detach write_attachment create_attachment_from(attachable) end end.purge_later end - def destroy_attachment - attachment.destroy - end - def create_attachment_from(attachable) ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable)) end diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index 96bc963cff..e645d868ce 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -98,6 +98,17 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "bar", blob.reload.metadata[:foo] end + test "detach blob" do + @user.avatar.attach create_blob(filename: "funky.jpg") + avatar_blob_id = @user.avatar.blob.id + avatar_key = @user.avatar.key + + @user.avatar.detach + assert_not @user.avatar.attached? + assert ActiveStorage::Blob.exists?(avatar_blob_id) + assert ActiveStorage::Blob.service.exist?(avatar_key) + end + test "purge attached blob" do @user.avatar.attach create_blob(filename: "funky.jpg") avatar_key = @user.avatar.key @@ -218,6 +229,21 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase end end + test "detach blobs" do + @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg") + highlight_blob_ids = @user.highlights.collect { |highlight| highlight.blob.id } + highlight_keys = @user.highlights.collect(&:key) + + @user.highlights.detach + assert_not @user.highlights.attached? + + assert ActiveStorage::Blob.exists?(highlight_blob_ids.first) + assert ActiveStorage::Blob.exists?(highlight_blob_ids.second) + + assert ActiveStorage::Blob.service.exist?(highlight_keys.first) + assert ActiveStorage::Blob.service.exist?(highlight_keys.second) + end + test "purge attached blobs" do @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg") highlight_keys = @user.highlights.collect(&:key) -- cgit v1.2.3 From d0c3062939dd214f9a4ad4bf304c0e461f689523 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 15 Nov 2017 01:10:25 +0900 Subject: CI against JRuby 9.1.14.0 JRuby 9.1.14.0 has been released and this version is available on Travis CI. http://jruby.org/2017/11/08/jruby-9-1-14-0 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 40af3dd044..290e0b5f2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,17 +106,17 @@ matrix: - "GEM=ar:postgresql POSTGRES=9.2" addons: postgresql: "9.2" - - rvm: jruby-9.1.13.0 + - rvm: jruby-9.1.14.0 jdk: oraclejdk8 env: - "GEM=ap" - - rvm: jruby-9.1.13.0 + - rvm: jruby-9.1.14.0 jdk: oraclejdk8 env: - "GEM=am,amo,aj" allow_failures: - rvm: ruby-head - - rvm: jruby-9.1.13.0 + - rvm: jruby-9.1.14.0 - env: "GEM=ac:integration" fast_finish: true -- cgit v1.2.3 From df82237a45e930a7ab53b95bee78a3f34c5b92fb Mon Sep 17 00:00:00 2001 From: Rich Date: Tue, 14 Nov 2017 19:24:00 +0000 Subject: Add a #populate method to migrations (#31082) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a #populate method to migrations * Address rubocop issues * Rename to #up_only and use #execute in the examples intead of the model * Update CHANGELOG [Rich Daley & Rafael Mendonça França] --- activerecord/CHANGELOG.md | 5 +++++ activerecord/lib/active_record/migration.rb | 18 ++++++++++++++++ .../test/cases/invertible_migration_test.rb | 25 ++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 57ec37c75b..2088b018d7 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Add `#only_up` to database migrations for code that is only relevant when + migrating up, e.g. populating a new column. + + *Rich Daley* + * Require raw SQL fragments to be explicitly marked when used in relation query methods. diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index c13efa9d70..67c8c9fc08 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -734,6 +734,24 @@ module ActiveRecord execute_block { yield helper } end + # Used to specify an operation that is only run when migrating up + # (for example, populating a new column with its initial values). + # + # In the following example, the new column `published` will be given + # the value `true` for all existing records. + # + # class AddPublishedToPosts < ActiveRecord::Migration[5.3] + # def change + # add_column :posts, :published, :boolean, default: false + # up_only do + # execute "update posts set published = 'true'" + # end + # end + # end + def up_only + execute_block { yield } unless reverting? + end + # Runs the given migration classes. # Last argument can specify options: # - :direction (default is :up) diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index 60c628511f..e0f40710eb 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -161,6 +161,13 @@ module ActiveRecord end end + class UpOnlyMigration < SilentMigration + def change + add_column :horses, :oldie, :boolean, default: false + up_only { execute "update horses set oldie = 'true'" } + end + end + setup do @verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false end @@ -378,5 +385,23 @@ module ActiveRecord "horses_index_named index should not exist" end end + + def test_up_only + InvertibleMigration.new.migrate(:up) + horse1 = Horse.create + # populates existing horses with oldie=true but new ones have default false + UpOnlyMigration.new.migrate(:up) + Horse.reset_column_information + horse1.reload + horse2 = Horse.create + + assert horse1.oldie? # created before migration + assert !horse2.oldie? # created after migration + + UpOnlyMigration.new.migrate(:down) # should be no error + connection = ActiveRecord::Base.connection + assert !connection.column_exists?(:horses, :oldie) + Horse.reset_column_information + end end end -- cgit v1.2.3 From 2e0fe5928f0d08f85b4796c85bd0b39f6be09079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 14 Nov 2017 14:11:29 -0500 Subject: Use released arel --- Gemfile | 2 -- Gemfile.lock | 15 ++++++--------- activerecord/activerecord.gemspec | 2 +- railties/lib/rails/generators/app_base.rb | 7 ++----- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Gemfile b/Gemfile index 3f8cd57853..f5a0ceeaad 100644 --- a/Gemfile +++ b/Gemfile @@ -6,8 +6,6 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } gemspec -gem "arel", github: "rails/arel" - # We need a newish Rake since Active Job sets its test tasks' descriptions. gem "rake", ">= 11.1" diff --git a/Gemfile.lock b/Gemfile.lock index 86f6ed3baf..eb73f2a4bf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,12 +34,6 @@ GIT event_emitter websocket -GIT - remote: https://github.com/rails/arel.git - revision: 42510bf71472e2e35d9becb546edd05562672344 - specs: - arel (9.0.0.alpha) - GIT remote: https://github.com/rails/sass-rails.git revision: bb5c1d34e8acad2e2960cc785184ffe17d7b3bca @@ -94,7 +88,7 @@ PATH activerecord (5.2.0.alpha) activemodel (= 5.2.0.alpha) activesupport (= 5.2.0.alpha) - arel (= 9.0.0.alpha) + arel (>= 9.0) activestorage (5.2.0.alpha) actionpack (= 5.2.0.alpha) activerecord (= 5.2.0.alpha) @@ -142,6 +136,7 @@ GEM amq-protocol (2.2.0) archive-zip (0.7.0) io-like (~> 0.3.0) + arel (9.0.0) ast (2.3.0) aws-partitions (1.20.0) aws-sdk-core (3.3.0) @@ -358,6 +353,7 @@ GEM mysql2 (0.4.9-x64-mingw32) mysql2 (0.4.9-x86-mingw32) nio4r (2.1.0) + nio4r (2.1.0-java) nokogiri (1.8.0) mini_portile2 (~> 2.2.0) nokogiri (1.8.0-java) @@ -503,6 +499,8 @@ GEM websocket (1.2.4) websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) + websocket-driver (0.6.5-java) + websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) xpath (2.1.0) nokogiri (~> 1.3) @@ -517,7 +515,6 @@ DEPENDENCIES activerecord-jdbcmysql-adapter (>= 1.3.0) activerecord-jdbcpostgresql-adapter (>= 1.3.0) activerecord-jdbcsqlite3-adapter (>= 1.3.0) - arel! aws-sdk-s3 azure-storage backburner @@ -579,4 +576,4 @@ DEPENDENCIES websocket-client-simple! BUNDLED WITH - 1.15.4 + 1.16.0 diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 7ad06fe840..8e42a11df4 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -31,5 +31,5 @@ Gem::Specification.new do |s| s.add_dependency "activesupport", version s.add_dependency "activemodel", version - s.add_dependency "arel", "9.0.0.alpha" + s.add_dependency "arel", ">= 9.0" end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index bdeddff645..73256bec61 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -266,17 +266,14 @@ module Rails end def rails_gemfile_entry - dev_edge_common = [ - GemfileEntry.github("arel", "rails/arel"), - ] if options.dev? [ GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH) - ] + dev_edge_common + ] elsif options.edge? [ GemfileEntry.github("rails", "rails/rails") - ] + dev_edge_common + ] else [GemfileEntry.version("rails", rails_version_specifier, -- cgit v1.2.3 From 79c3d5a67d095379f72746b7b9f485861cdf9d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 14 Nov 2017 14:42:12 -0500 Subject: Use released sass-rails --- Gemfile | 2 +- Gemfile.lock | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Gemfile b/Gemfile index f5a0ceeaad..9ff34dc26c 100644 --- a/Gemfile +++ b/Gemfile @@ -17,7 +17,7 @@ gem "capybara", "~> 2.15" gem "rack-cache", "~> 1.2" gem "coffee-rails" -gem "sass-rails", github: "rails/sass-rails", branch: "5-0-stable" +gem "sass-rails" gem "turbolinks", "~> 5" # require: false so bcrypt is loaded only when has_secure_password is used. diff --git a/Gemfile.lock b/Gemfile.lock index eb73f2a4bf..23034d7c26 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,18 +34,6 @@ GIT event_emitter websocket -GIT - remote: https://github.com/rails/sass-rails.git - revision: bb5c1d34e8acad2e2960cc785184ffe17d7b3bca - branch: 5-0-stable - specs: - 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) - GIT remote: https://github.com/robin850/sdoc.git revision: 0e340352f3ab2f196c8a8743f83c2ee286e4f71c @@ -423,11 +411,17 @@ GEM rubyzip (1.2.1) rufus-scheduler (3.4.2) et-orbi (~> 1.0) - sass (3.5.1) + sass (3.5.3) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) + sass-rails (5.0.7) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) selenium-webdriver (3.5.1) childprocess (~> 0.5) rubyzip (~> 1.0) @@ -559,7 +553,7 @@ DEPENDENCIES resque resque-scheduler! rubocop (>= 0.47) - sass-rails! + sass-rails sdoc! sequel sidekiq -- cgit v1.2.3 From 46a2f93614ccf0d1628e6fc3c4666cee476d17c8 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Tue, 14 Nov 2017 20:02:52 +0000 Subject: Fix migration version in doc of #up_only --- activerecord/lib/active_record/migration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 67c8c9fc08..360bf25a8c 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -740,7 +740,7 @@ module ActiveRecord # In the following example, the new column `published` will be given # the value `true` for all existing records. # - # class AddPublishedToPosts < ActiveRecord::Migration[5.3] + # class AddPublishedToPosts < ActiveRecord::Migration[5.2] # def change # add_column :posts, :published, :boolean, default: false # up_only do -- cgit v1.2.3 From 703478d3f17a85c65db9d77d1f849d6b1943e870 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Tue, 14 Nov 2017 21:09:34 +0000 Subject: Fix activesupport/CHANGELOG.md [ci skip] --- activesupport/CHANGELOG.md | 56 ++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 24c7f362e9..4762c7d21d 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,32 +1,34 @@ * Redis cache store. - # Defaults to `redis://localhost:6379/0`. Only use for dev/test. - config.cache_store = :redis_cache_store - - # Supports all common cache store options (:namespace, :compress, - # :compress_threshold, :expires_in, :race_condition_tool) and all - # Redis options. - cache_password = Rails.application.secrets.redis_cache_password - config.cache_store = :redis_cache_store, driver: :hiredis, - namespace: 'myapp-cache', compress: true, timeout: 1, - url: "redis://:#{cache_password}@myapp-cache-1:6379/0" - - # Supports Redis::Distributed with multiple hosts - config.cache_store = :redis_cache_store, driver: :hiredis - namespace: 'myapp-cache', compress: true, - url: %w[ - redis://myapp-cache-1:6379/0 - redis://myapp-cache-1:6380/0 - redis://myapp-cache-2:6379/0 - redis://myapp-cache-2:6380/0 - redis://myapp-cache-3:6379/0 - redis://myapp-cache-3:6380/0 - ] - - # Or pass a builder block - config.cache_store = :redis_cache_store, - namespace: 'myapp-cache', compress: true, - redis: -> { Redis.new … } + ``` + # Defaults to `redis://localhost:6379/0`. Only use for dev/test. + config.cache_store = :redis_cache_store + + # Supports all common cache store options (:namespace, :compress, + # :compress_threshold, :expires_in, :race_condition_tool) and all + # Redis options. + cache_password = Rails.application.secrets.redis_cache_password + config.cache_store = :redis_cache_store, driver: :hiredis, + namespace: 'myapp-cache', compress: true, timeout: 1, + url: "redis://:#{cache_password}@myapp-cache-1:6379/0" + + # Supports Redis::Distributed with multiple hosts + config.cache_store = :redis_cache_store, driver: :hiredis + namespace: 'myapp-cache', compress: true, + url: %w[ + redis://myapp-cache-1:6379/0 + redis://myapp-cache-1:6380/0 + redis://myapp-cache-2:6379/0 + redis://myapp-cache-2:6380/0 + redis://myapp-cache-3:6379/0 + redis://myapp-cache-3:6380/0 + ] + + # Or pass a builder block + config.cache_store = :redis_cache_store, + namespace: 'myapp-cache', compress: true, + redis: -> { Redis.new … } + ``` Deployment note: Take care to use a *dedicated Redis cache* rather than pointing this at your existing Redis server. It won't cope well -- cgit v1.2.3 From ca68f1cf49a3689c276d502105aaa68fa106502d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 15 Nov 2017 07:40:59 +0900 Subject: Fix typo s/only_up/up_only/ [ci skip] --- activerecord/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 2088b018d7..217eada1d7 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,4 +1,4 @@ -* Add `#only_up` to database migrations for code that is only relevant when +* Add `#up_only` to database migrations for code that is only relevant when migrating up, e.g. populating a new column. *Rich Daley* -- cgit v1.2.3 From 6a34ef52c9eda2bc9ef85325bfa0a43cb4afd724 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 15 Nov 2017 08:16:27 +0900 Subject: Fix CI failure due to invalid `up_only` for MySQL `oldie = 'true'` to `tinyint(1)` column is invalid value for MySQL: ``` Mysql2::Error: Incorrect integer value: 'true' for column 'oldie' at row 1: update horses set oldie = 'true' ``` --- activerecord/test/cases/invertible_migration_test.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index e0f40710eb..ebe0b0aa87 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -163,11 +163,13 @@ module ActiveRecord class UpOnlyMigration < SilentMigration def change - add_column :horses, :oldie, :boolean, default: false - up_only { execute "update horses set oldie = 'true'" } + add_column :horses, :oldie, :integer, default: 0 + up_only { execute "update horses set oldie = 1" } end end + self.use_transactional_tests = false + setup do @verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false end @@ -389,14 +391,14 @@ module ActiveRecord def test_up_only InvertibleMigration.new.migrate(:up) horse1 = Horse.create - # populates existing horses with oldie=true but new ones have default false + # populates existing horses with oldie = 1 but new ones have default 0 UpOnlyMigration.new.migrate(:up) Horse.reset_column_information horse1.reload horse2 = Horse.create - assert horse1.oldie? # created before migration - assert !horse2.oldie? # created after migration + assert 1, horse1.oldie # created before migration + assert 0, horse2.oldie # created after migration UpOnlyMigration.new.migrate(:down) # should be no error connection = ActiveRecord::Base.connection -- cgit v1.2.3 From 570ae8f8735f32282cf790eb9d8196b007130658 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 15 Nov 2017 09:09:15 +0900 Subject: Fix asset url examples [ci skip] --- actionview/lib/action_view/helpers/asset_url_helper.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb index a4dcfc9a6c..f7690104ee 100644 --- a/actionview/lib/action_view/helpers/asset_url_helper.rb +++ b/actionview/lib/action_view/helpers/asset_url_helper.rb @@ -324,7 +324,7 @@ module ActionView # Since +javascript_url+ is based on +asset_url+ method you can set :host options. If :host # options is set, it overwrites global +config.action_controller.asset_host+ setting. # - # javascript_url "js/xmlhr.js", host: "http://stage.example.com" # => http://stage.example.com/assets/dir/xmlhr.js + # javascript_url "js/xmlhr.js", host: "http://stage.example.com" # => http://stage.example.com/assets/js/xmlhr.js # def javascript_url(source, options = {}) url_to_asset(source, { type: :javascript }.merge!(options)) @@ -351,7 +351,7 @@ module ActionView # Since +stylesheet_url+ is based on +asset_url+ method you can set :host options. If :host # options is set, it overwrites global +config.action_controller.asset_host+ setting. # - # stylesheet_url "css/style.css", host: "http://stage.example.com" # => http://stage.example.com/css/style.css + # stylesheet_url "css/style.css", host: "http://stage.example.com" # => http://stage.example.com/assets/css/style.css # def stylesheet_url(source, options = {}) url_to_asset(source, { type: :stylesheet }.merge!(options)) @@ -381,7 +381,7 @@ module ActionView # Since +image_url+ is based on +asset_url+ method you can set :host options. If :host # options is set, it overwrites global +config.action_controller.asset_host+ setting. # - # image_url "edit.png", host: "http://stage.example.com" # => http://stage.example.com/edit.png + # image_url "edit.png", host: "http://stage.example.com" # => http://stage.example.com/assets/edit.png # def image_url(source, options = {}) url_to_asset(source, { type: :image }.merge!(options)) @@ -407,7 +407,7 @@ module ActionView # Since +video_url+ is based on +asset_url+ method you can set :host options. If :host # options is set, it overwrites global +config.action_controller.asset_host+ setting. # - # video_url "hd.avi", host: "http://stage.example.com" # => http://stage.example.com/hd.avi + # video_url "hd.avi", host: "http://stage.example.com" # => http://stage.example.com/videos/hd.avi # def video_url(source, options = {}) url_to_asset(source, { type: :video }.merge!(options)) @@ -433,7 +433,7 @@ module ActionView # Since +audio_url+ is based on +asset_url+ method you can set :host options. If :host # options is set, it overwrites global +config.action_controller.asset_host+ setting. # - # audio_url "horse.wav", host: "http://stage.example.com" # => http://stage.example.com/horse.wav + # audio_url "horse.wav", host: "http://stage.example.com" # => http://stage.example.com/audios/horse.wav # def audio_url(source, options = {}) url_to_asset(source, { type: :audio }.merge!(options)) @@ -458,7 +458,7 @@ module ActionView # Since +font_url+ is based on +asset_url+ method you can set :host options. If :host # options is set, it overwrites global +config.action_controller.asset_host+ setting. # - # font_url "font.ttf", host: "http://stage.example.com" # => http://stage.example.com/font.ttf + # font_url "font.ttf", host: "http://stage.example.com" # => http://stage.example.com/fonts/font.ttf # def font_url(source, options = {}) url_to_asset(source, { type: :font }.merge!(options)) -- cgit v1.2.3 From 9d87a172ca32d60526f2b4c4ace0c5dc333f8489 Mon Sep 17 00:00:00 2001 From: Kelly Stannard Date: Tue, 14 Nov 2017 19:26:03 -0500 Subject: Update configuring.md It was brought to my attention that the Rails guide suggests using filenames to ensure code load order, so I thought I would suggest a better alternative. --- guides/source/configuring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 2d03f0a61e..6e129a5680 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -1057,7 +1057,7 @@ After loading the framework and any gems in your application, Rails turns to loa NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the initializers folder on down. -TIP: If you have any ordering dependency in your initializers, you can control the load order through naming. Initializer files are loaded in alphabetical order by their path. For example, `01_critical.rb` will be loaded before `02_normal.rb`. +TIP: While Rails supports numbering of initializer file names for load ordering purposes, a better technique is to place any code that need to load in a specific order within the same file. This reduces file name churn, makes dependencies more explicit, and can help surface new concepts within your application. Initialization events --------------------- -- cgit v1.2.3 From 2a26ff80e8a08c2ac22332b52f6534cb6534bae1 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 15 Nov 2017 10:00:43 +0900 Subject: Use released arel Related to 2e0fe5928f0d08f85b4796c85bd0b39f6be09079 --- guides/bug_report_templates/action_controller_master.rb | 1 - guides/bug_report_templates/active_job_master.rb | 1 - guides/bug_report_templates/active_record_master.rb | 1 - guides/bug_report_templates/active_record_migrations_master.rb | 1 - guides/bug_report_templates/benchmark.rb | 1 - guides/bug_report_templates/generic_master.rb | 1 - 6 files changed, 6 deletions(-) diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb index 932d329943..732cdad259 100644 --- a/guides/bug_report_templates/action_controller_master.rb +++ b/guides/bug_report_templates/action_controller_master.rb @@ -15,7 +15,6 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } 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 36d9137b71..c0c67879f3 100644 --- a/guides/bug_report_templates/active_job_master.rb +++ b/guides/bug_report_templates/active_job_master.rb @@ -15,7 +15,6 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } 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 b66deb36f3..b1c83a51f6 100644 --- a/guides/bug_report_templates/active_record_master.rb +++ b/guides/bug_report_templates/active_record_master.rb @@ -15,7 +15,6 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } 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 737ce66d7b..0979a42a41 100644 --- a/guides/bug_report_templates/active_record_migrations_master.rb +++ b/guides/bug_report_templates/active_record_migrations_master.rb @@ -15,7 +15,6 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } 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 f5c88086a9..520c5e8bab 100644 --- a/guides/bug_report_templates/benchmark.rb +++ b/guides/bug_report_templates/benchmark.rb @@ -15,7 +15,6 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } 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 240571ba9a..f7c9fedf02 100644 --- a/guides/bug_report_templates/generic_master.rb +++ b/guides/bug_report_templates/generic_master.rb @@ -15,7 +15,6 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "rails", github: "rails/rails" - gem "arel", github: "rails/arel" end require "active_support" -- cgit v1.2.3 From 23c41e4657a507c574e555339821f545c819e602 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Tue, 14 Nov 2017 17:15:33 +0900 Subject: These strings should already be frozen where ruby accepts the magic-comment --- activesupport/test/core_ext/object/instance_variables_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/test/core_ext/object/instance_variables_test.rb b/activesupport/test/core_ext/object/instance_variables_test.rb index b9ec827954..a3d8daab5b 100644 --- a/activesupport/test/core_ext/object/instance_variables_test.rb +++ b/activesupport/test/core_ext/object/instance_variables_test.rb @@ -19,7 +19,7 @@ class ObjectInstanceVariableTest < ActiveSupport::TestCase end def test_instance_exec_passes_arguments_to_block - assert_equal %w(hello goodbye), "hello".instance_exec("goodbye") { |v| [self, v] } + assert_equal %w(hello goodbye), "hello".dup.instance_exec("goodbye") { |v| [self, v] } end def test_instance_exec_with_frozen_obj @@ -27,7 +27,7 @@ class ObjectInstanceVariableTest < ActiveSupport::TestCase end def test_instance_exec_nested - assert_equal %w(goodbye olleh bar), "hello".instance_exec("goodbye") { |arg| + assert_equal %w(goodbye olleh bar), "hello".dup.instance_exec("goodbye") { |arg| [arg] + instance_exec("bar") { |v| [reverse, v] } } end end -- cgit v1.2.3 From 2eea6458a14d99b19dffe8673015cde0800f128a Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 11 Nov 2017 15:19:29 +0000 Subject: Handle `TZInfo::AmbiguousTime` errors Make `ActiveSupport::TimeWithZone` match Ruby's handling of ambiguous times by choosing the later period, e.g. Ruby: ``` ENV["TZ"] = "Europe/Moscow" Time.local(2014, 10, 26, 1, 0, 0) # => 2014-10-26 01:00:00 +0300 ``` Before: ``` >> "2014-10-26 01:00:00".in_time_zone("Moscow") TZInfo::AmbiguousTime: 26/10/2014 01:00 is an ambiguous local time. ``` After: ``` >> "2014-10-26 01:00:00".in_time_zone("Moscow") => Sun, 26 Oct 2014 01:00:00 MSK +03:00 ``` Fixes #17395. --- activesupport/CHANGELOG.md | 27 ++++++++++++++++++++++ .../lib/active_support/values/time_zone.rb | 2 +- activesupport/test/core_ext/time_with_zone_test.rb | 12 ++++++++++ activesupport/test/time_zone_test.rb | 26 +++++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 4762c7d21d..904dab0e05 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,30 @@ +* Handle `TZInfo::AmbiguousTime` errors + + Make `ActiveSupport::TimeWithZone` match Ruby's handling of ambiguous + times by choosing the later period, e.g. + + Ruby: + ``` + ENV["TZ"] = "Europe/Moscow" + Time.local(2014, 10, 26, 1, 0, 0) # => 2014-10-26 01:00:00 +0300 + ``` + + Before: + ``` + >> "2014-10-26 01:00:00".in_time_zone("Moscow") + TZInfo::AmbiguousTime: 26/10/2014 01:00 is an ambiguous local time. + ``` + + After: + ``` + >> "2014-10-26 01:00:00".in_time_zone("Moscow") + => Sun, 26 Oct 2014 01:00:00 MSK +03:00 + ``` + + Fixes #17395. + + *Andrew White* + * Redis cache store. ``` diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 4d1fbd4453..639a066e28 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -506,7 +506,7 @@ module ActiveSupport # Available so that TimeZone instances respond like TZInfo::Timezone # instances. def period_for_local(time, dst = true) - tzinfo.period_for_local(time, dst) + tzinfo.period_for_local(time, dst) { |periods| periods.last } end def periods_for_local(time) #:nodoc: diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 0f80a24758..ab96568956 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -50,6 +50,12 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_raise(ArgumentError) { @twz.in_time_zone(Object.new) } end + def test_in_time_zone_with_ambiguous_time + with_env_tz "Europe/Moscow" do + assert_equal Time.utc(2014, 10, 25, 22, 0, 0), Time.local(2014, 10, 26, 1, 0, 0).in_time_zone("Moscow") + end + end + def test_localtime assert_equal @twz.localtime, @twz.utc.getlocal assert_instance_of Time, @twz.localtime @@ -1301,4 +1307,10 @@ class TimeWithZoneMethodsForString < ActiveSupport::TestCase assert_raise(ArgumentError) { @u.in_time_zone(Object.new) } assert_raise(ArgumentError) { @z.in_time_zone(Object.new) } end + + def test_in_time_zone_with_ambiguous_time + with_tz_default "Moscow" do + assert_equal Time.utc(2014, 10, 25, 22, 0, 0), "2014-10-26 01:00:00".in_time_zone + end + end end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index acb0ecd226..862e872494 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -32,6 +32,12 @@ class TimeZoneTest < ActiveSupport::TestCase end end + def test_period_for_local_with_ambigiuous_time + zone = ActiveSupport::TimeZone["Moscow"] + period = zone.period_for_local(Time.utc(2015, 1, 1)) + assert_equal period, zone.period_for_local(Time.utc(2014, 10, 26, 1, 0, 0)) + end + def test_from_integer_to_map assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[-28800] # PST end @@ -195,6 +201,11 @@ class TimeZoneTest < ActiveSupport::TestCase assert_equal "EDT", twz.zone end + def test_local_with_ambiguous_time + zone = ActiveSupport::TimeZone["Moscow"] + assert_equal Time.utc(2014, 10, 25, 22, 0, 0), zone.local(2014, 10, 26, 1, 0, 0) + end + def test_at zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] secs = 946684800.0 @@ -303,6 +314,11 @@ class TimeZoneTest < ActiveSupport::TestCase end end + def test_iso8601_with_ambiguous_time + zone = ActiveSupport::TimeZone["Moscow"] + assert_equal Time.utc(2014, 10, 25, 22, 0, 0), zone.parse("2014-10-26T01:00:00") + end + def test_parse zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] twz = zone.parse("1999-12-31 19:00:00") @@ -412,6 +428,11 @@ class TimeZoneTest < ActiveSupport::TestCase assert_equal "argument out of range", exception.message end + def test_parse_with_ambiguous_time + zone = ActiveSupport::TimeZone["Moscow"] + assert_equal Time.utc(2014, 10, 25, 22, 0, 0), zone.parse("2014-10-26 01:00:00") + end + def test_rfc3339 zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"] twz = zone.rfc3339("1999-12-31T14:00:00-10:00") @@ -604,6 +625,11 @@ class TimeZoneTest < ActiveSupport::TestCase end end + def test_strptime_with_ambiguous_time + zone = ActiveSupport::TimeZone["Moscow"] + assert_equal Time.utc(2014, 10, 25, 22, 0, 0), zone.strptime("2014-10-26 01:00:00", "%Y-%m-%d %H:%M:%S") + end + 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) -- cgit v1.2.3 From ef0a3d0dfa1b0d7ce8234c5a03db6af64f8b40e0 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 15 Nov 2017 17:00:51 +0000 Subject: Allow test_ignored_columns_not_included_in_SELECT column names case insensitive i.e. Oracle database identifier is UPPERCASE unlike other databases. ```ruby (byebug) query = Developer.all.to_sql "SELECT \"DEVELOPERS\".\"ID\", \"DEVELOPERS\".\"NAME\", \"DEVELOPERS\".\"SALARY\", \"DEVELOPERS\".\"FIRM_ID\", \"DEVELOPERS\".\"MENTOR_ID\", \"DEVELOPERS\".\"CREATED_AT\", \"DEVELOPERS\".\"UPDATED_AT\", \"DEVELOPERS\".\"CREATED_ON\", \"DEVELOPERS\".\"UPDATED_ON\" FROM \"DEVELOPERS\"" ``` --- activerecord/test/cases/base_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 0ae88ee6a0..d79afa2ee9 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1489,7 +1489,7 @@ class BasicsTest < ActiveRecord::TestCase end test "ignored columns not included in SELECT" do - query = Developer.all.to_sql + query = Developer.all.to_sql.downcase # ignored column refute query.include?("first_name") -- cgit v1.2.3 From 415d0543a527dcd2e099dcd819c6938f3dcac54a Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Wed, 15 Nov 2017 21:14:25 +0100 Subject: Go through a single credentials instance. Instead of stashing the values in constants. --- .../rails/credentials/credentials_generator.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb index 52cb4bd8bf..ab15da5423 100644 --- a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb +++ b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb @@ -7,14 +7,11 @@ require "active_support/encrypted_configuration" module Rails module Generators class CredentialsGenerator < Base - CONFIG_PATH = "config/credentials.yml.enc" - KEY_PATH = "config/master.key" - def add_credentials_file - unless File.exist?(CONFIG_PATH) + unless credentials.exist? template = credentials_template - say "Adding #{CONFIG_PATH} to store encrypted credentials." + say "Adding #{credentials.content_path} to store encrypted credentials." say "" say "The following content has been encrypted with the Rails master key:" say "" @@ -29,13 +26,17 @@ module Rails end def add_credentials_file_silently(template = nil) - unless File.exist?(CONFIG_PATH) - setup = { config_path: CONFIG_PATH, key_path: KEY_PATH, env_key: "RAILS_MASTER_KEY" } - ActiveSupport::EncryptedConfiguration.new(setup).write(credentials_template) - end + credentials.write(credentials_template) end private + def credentials + ActiveSupport::EncryptedConfiguration.new \ + config_path: "config/credentials.yml.enc", + key_path: "config/master.key", + env_key: "RAILS_MASTER_KEY" + end + def credentials_template "# aws:\n# access_key_id: 123\n# secret_access_key: 345\n\n" + "# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.\n" + -- cgit v1.2.3 From 7a8728a03986489e1c843ed850afc2c16fb6eb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Wn=C4=99trzak?= Date: Tue, 14 Nov 2017 11:44:23 +0100 Subject: Add CLI to manage encrypted files/configs. To edit/show encrypted file: ``` bin/rails encrypted:edit config/staging_tokens.yml.enc bin/rails encrypted:edit config/staging_tokens.yml.enc --key config/staging.key bin/rails encrypted:show config/staging_tokens.yml.enc ``` Also provides a backing Rails.application.encrypted API for Ruby access: ```ruby Rails.application.encrypted("config/staging_tokens.yml.enc").read Rails.application.encrypted("config/staging_tokens.yml.enc").config Rails.application.encrypted("config/staging_tokens.yml.enc", key: "config/staging.key") ``` --- railties/lib/rails/application.rb | 39 +++++++++-- railties/lib/rails/command/helpers/editor.rb | 33 +++++++++ .../commands/credentials/credentials_command.rb | 29 +++----- .../rails/commands/encrypted/encrypted_command.rb | 77 +++++++++++++++++++++ .../rails/credentials/credentials_generator.rb | 19 ++--- .../encrypted_file/encrypted_file_generator.rb | 38 ++++++++++ .../encryption_key_file_generator.rb | 53 ++++++++++++++ .../rails/master_key/master_key_generator.rb | 24 +++---- railties/test/commands/credentials_test.rb | 5 +- railties/test/commands/encrypted_test.rb | 80 ++++++++++++++++++++++ 10 files changed, 347 insertions(+), 50 deletions(-) create mode 100644 railties/lib/rails/command/helpers/editor.rb create mode 100644 railties/lib/rails/commands/encrypted/encrypted_command.rb create mode 100644 railties/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb create mode 100644 railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb create mode 100644 railties/test/commands/encrypted_test.rb diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index ade8cb6a48..31bc542308 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -433,10 +433,41 @@ module Rails # the Rails master key, which is either taken from ENV["RAILS_MASTER_KEY"] or from loading # `config/master.key`. def credentials - @credentials ||= ActiveSupport::EncryptedConfiguration.new \ - config_path: Rails.root.join("config/credentials.yml.enc"), - key_path: Rails.root.join("config/master.key"), - env_key: "RAILS_MASTER_KEY" + @credentials ||= encrypted("config/credentials.yml.enc") + end + + # Shorthand to decrypt any encrypted configurations or files. + # + # For any file added with `bin/rails encrypted:edit` call `read` to decrypt + # the file with the master key. + # The master key is either stored in `config/master.key` or `ENV["RAILS_MASTER_KEY"]`. + # + # Rails.application.encrypted("config/mystery_man.key").read + # # => "We've met before, haven't we?" + # + # It's also possible to interpret encrypted YAML files with `config`. + # + # Rails.application.encrypted("config/credentials.yml.enc").config + # # => { next_guys_line: "I don't think so. Where was it you think we met?" } + # + # Any top-level configs are also accessible directly on the return value: + # + # Rails.application.encrypted("config/credentials.yml.enc").next_guys_line + # # => "I don't think so. Where was it you think we met?" + # + # The files or configs can also be encrypted with a custom key. To decrypt with + # a key in the `ENV`, use: + # + # Rails.application.encrypted("config/special_tokens.yml.enc", env_key: "SPECIAL_TOKENS") + # + # Or to decrypt with a file, that should be version control ignored, relative to `Rails.root`: + # + # Rails.application.encrypted("config/special_tokens.yml.enc", key_path: "config/special_tokens.key") + def encrypted(path, key_path: "config/master.key", env_key: "RAILS_MASTER_KEY") + ActiveSupport::EncryptedConfiguration.new \ + config_path: Rails.root.join(path), + key_path: Rails.root.join(key_path), + env_key: env_key end def to_app #:nodoc: diff --git a/railties/lib/rails/command/helpers/editor.rb b/railties/lib/rails/command/helpers/editor.rb new file mode 100644 index 0000000000..5e9ecc05e7 --- /dev/null +++ b/railties/lib/rails/command/helpers/editor.rb @@ -0,0 +1,33 @@ +require "active_support/encrypted_file" + +module Rails + module Command + module Helpers + module Editor + private + def ensure_editor_available(command:) + if ENV["EDITOR"].to_s.empty? + say "No $EDITOR to open file in. Assign one like this:" + say "" + say %(EDITOR="mate --wait" #{command}) + say "" + say "For editors that fork and exit immediately, it's important to pass a wait flag," + say "otherwise the credentials will be saved immediately with no chance to edit." + + false + else + true + end + end + + def catch_editing_exceptions + yield + rescue Interrupt + say "Aborted changing file: nothing saved." + rescue ActiveSupport::EncryptedFile::MissingKeyError => error + say error.message + end + end + end + end +end diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb index e5d3d01431..8085f07c2b 100644 --- a/railties/lib/rails/commands/credentials/credentials_command.rb +++ b/railties/lib/rails/commands/credentials/credentials_command.rb @@ -1,10 +1,13 @@ # frozen_string_literal: true require "active_support" +require "rails/command/helpers/editor" module Rails module Command class CredentialsCommand < Rails::Command::Base # :nodoc: + include Helpers::Editor + no_commands do def help say "Usage:\n #{self.class.banner}" @@ -16,41 +19,25 @@ module Rails def edit require_application_and_environment! - ensure_editor_available || (return) + ensure_editor_available(command: "bin/rails credentials:edit") || (return) ensure_master_key_has_been_added ensure_credentials_have_been_added - change_credentials_in_system_editor + catch_editing_exceptions do + change_credentials_in_system_editor + end say "New credentials encrypted and saved." - rescue Interrupt - say "Aborted changing credentials: nothing saved." - rescue ActiveSupport::EncryptedFile::MissingKeyError => error - say error.message end def show require_application_and_environment! + say Rails.application.credentials.read.presence || "No credentials have been added yet. Use bin/rails credentials:edit to change that." end private - def ensure_editor_available - if ENV["EDITOR"].to_s.empty? - say "No $EDITOR to open credentials in. Assign one like this:" - say "" - say %(EDITOR="mate --wait" bin/rails credentials:edit) - say "" - say "For editors that fork and exit immediately, it's important to pass a wait flag," - say "otherwise the credentials will be saved immediately with no chance to edit." - - false - else - true - end - end - def ensure_master_key_has_been_added master_key_generator.add_master_key_file master_key_generator.ignore_master_key_file diff --git a/railties/lib/rails/commands/encrypted/encrypted_command.rb b/railties/lib/rails/commands/encrypted/encrypted_command.rb new file mode 100644 index 0000000000..898094f1a4 --- /dev/null +++ b/railties/lib/rails/commands/encrypted/encrypted_command.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require "pathname" +require "active_support" +require "rails/command/helpers/editor" + +module Rails + module Command + class EncryptedCommand < Rails::Command::Base # :nodoc: + include Helpers::Editor + + class_option :key, aliases: "-k", type: :string, + default: "config/master.key", desc: "The Rails.root relative path to the encryption key" + + no_commands do + def help + say "Usage:\n #{self.class.banner}" + say "" + end + end + + def edit(file_path) + require_application_and_environment! + + ensure_editor_available(command: "bin/rails encrypted:edit") || (return) + ensure_encryption_key_has_been_added(options[:key]) + ensure_encrypted_file_has_been_added(file_path, options[:key]) + + catch_editing_exceptions do + change_encrypted_file_in_system_editor(file_path, options[:key]) + end + + say "File encrypted and saved." + rescue ActiveSupport::MessageEncryptor::InvalidMessage + say "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?" + end + + def show(file_path) + require_application_and_environment! + + say Rails.application.encrypted(file_path, key_path: options[:key]).read.presence || + "File '#{file_path}' does not exist. Use bin/rails encrypted:edit #{file_path} to change that." + end + + private + def ensure_encryption_key_has_been_added(key_path) + encryption_key_file_generator.add_key_file(key_path) + encryption_key_file_generator.ignore_key_file(key_path) + end + + def ensure_encrypted_file_has_been_added(file_path, key_path) + encrypted_file_generator.add_encrypted_file_silently(file_path, key_path) + end + + def change_encrypted_file_in_system_editor(file_path, key_path) + Rails.application.encrypted(file_path, key_path: key_path).change do |tmp_path| + system("#{ENV["EDITOR"]} #{tmp_path}") + end + end + + + def encryption_key_file_generator + require "rails/generators" + require "rails/generators/rails/encryption_key_file/encryption_key_file_generator" + + Rails::Generators::EncryptionKeyFileGenerator.new + end + + def encrypted_file_generator + require "rails/generators" + require "rails/generators/rails/encrypted_file/encrypted_file_generator" + + Rails::Generators::EncryptedFileGenerator.new + end + end + end +end diff --git a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb index 52cb4bd8bf..ab15da5423 100644 --- a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb +++ b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb @@ -7,14 +7,11 @@ require "active_support/encrypted_configuration" module Rails module Generators class CredentialsGenerator < Base - CONFIG_PATH = "config/credentials.yml.enc" - KEY_PATH = "config/master.key" - def add_credentials_file - unless File.exist?(CONFIG_PATH) + unless credentials.exist? template = credentials_template - say "Adding #{CONFIG_PATH} to store encrypted credentials." + say "Adding #{credentials.content_path} to store encrypted credentials." say "" say "The following content has been encrypted with the Rails master key:" say "" @@ -29,13 +26,17 @@ module Rails end def add_credentials_file_silently(template = nil) - unless File.exist?(CONFIG_PATH) - setup = { config_path: CONFIG_PATH, key_path: KEY_PATH, env_key: "RAILS_MASTER_KEY" } - ActiveSupport::EncryptedConfiguration.new(setup).write(credentials_template) - end + credentials.write(credentials_template) end private + def credentials + ActiveSupport::EncryptedConfiguration.new \ + config_path: "config/credentials.yml.enc", + key_path: "config/master.key", + env_key: "RAILS_MASTER_KEY" + end + def credentials_template "# aws:\n# access_key_id: 123\n# secret_access_key: 345\n\n" + "# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.\n" + diff --git a/railties/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb b/railties/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb new file mode 100644 index 0000000000..ddce5f6fe2 --- /dev/null +++ b/railties/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "rails/generators/base" +require "active_support/encrypted_file" + +module Rails + module Generators + class EncryptedFileGenerator < Base + def add_encrypted_file(file_path, key_path) + unless File.exist?(file_path) + say "Adding #{file_path} to store encrypted content." + say "" + say "The following content has been encrypted with the encryption key:" + say "" + say template, :on_green + say "" + + add_encrypted_file_silently(file_path, key_path) + + say "You can edit encrypted file with `bin/rails encrypted:edit #{file_path}`." + say "" + end + end + + def add_encrypted_file_silently(file_path, key_path, template = encrypted_file_template) + unless File.exist?(file_path) + setup = { content_path: file_path, key_path: key_path, env_key: "RAILS_MASTER_KEY" } + ActiveSupport::EncryptedFile.new(setup).write(template) + end + end + + private + def encrypted_file_template + "# aws:\n# access_key_id: 123\n# secret_access_key: 345\n\n" + end + end + end +end diff --git a/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb new file mode 100644 index 0000000000..dd0d0c6c66 --- /dev/null +++ b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require "pathname" +require "rails/generators/base" +require "active_support/encrypted_file" + +module Rails + module Generators + class EncryptionKeyFileGenerator < Base + def add_key_file(key_path) + key_path = Pathname.new(key_path) + + unless key_path.exist? + key = ActiveSupport::EncryptedFile.generate_key + + log "Adding #{key_path} to store the encryption key: #{key}" + log "" + log "Save this in a password manager your team can access." + log "" + log "If you lose the key, no one, including you, can access anything encrypted with it." + + log "" + add_key_file_silently(key_path, key) + log "" + end + end + + def add_key_file_silently(key_path, key = nil) + create_file key_path, key || ActiveSupport::EncryptedFile.generate_key + end + + def ignore_key_file(key_path, ignore: key_ignore(key_path)) + if File.exist?(".gitignore") + unless File.read(".gitignore").include?(ignore) + log "Ignoring #{key_path} so it won't end up in Git history:" + log "" + append_to_file ".gitignore", ignore + log "" + end + else + log "IMPORTANT: Don't commit #{key_path}. Add this to your ignore file:" + log ignore, :on_green + log "" + end + end + + private + def key_ignore(key_path) + [ "", "/#{key_path}", "" ].join("\n") + end + end + end +end diff --git a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb index 29d83f5d81..e57f07c1ae 100644 --- a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb +++ b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true -require "rails/generators/base" require "pathname" +require "rails/generators/base" +require "rails/generators/rails/encryption_key_file/encryption_key_file_generator" require "active_support/encrypted_file" module Rails @@ -20,31 +21,24 @@ module Rails log "If you lose the key, no one, including you, can access anything encrypted with it." log "" - add_master_key_file_silently key + add_master_key_file_silently(key) log "" end end def add_master_key_file_silently(key = nil) - create_file MASTER_KEY_PATH, key || ActiveSupport::EncryptedFile.generate_key + key_file_generator.add_key_file_silently(MASTER_KEY_PATH, key) end def ignore_master_key_file - if File.exist?(".gitignore") - unless File.read(".gitignore").include?(key_ignore) - log "Ignoring #{MASTER_KEY_PATH} so it won't end up in Git history:" - log "" - append_to_file ".gitignore", key_ignore - log "" - end - else - log "IMPORTANT: Don't commit #{MASTER_KEY_PATH}. Add this to your ignore file:" - log key_ignore, :on_green - log "" - end + key_file_generator.ignore_key_file(MASTER_KEY_PATH, ignore: key_ignore) end private + def key_file_generator + EncryptionKeyFileGenerator.new + end + def key_ignore [ "", "# Ignore master key for decrypting credentials and more.", "/#{MASTER_KEY_PATH}", "" ].join("\n") end diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb index 743fb5f788..4ef827fcf1 100644 --- a/railties/test/commands/credentials_test.rb +++ b/railties/test/commands/credentials_test.rb @@ -13,7 +13,10 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase teardown { teardown_app } test "edit without editor gives hint" do - assert_match "No $EDITOR to open credentials in", run_edit_command(editor: "") + run_edit_command(editor: "").tap do |output| + assert_match "No $EDITOR to open file in", output + assert_match "bin/rails credentials:edit", output + end end test "edit credentials" do diff --git a/railties/test/commands/encrypted_test.rb b/railties/test/commands/encrypted_test.rb new file mode 100644 index 0000000000..0461493f2a --- /dev/null +++ b/railties/test/commands/encrypted_test.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "env_helpers" +require "rails/command" +require "rails/commands/encrypted/encrypted_command" + +class Rails::Command::EncryptedCommandTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation, EnvHelpers + + setup :build_app + teardown :teardown_app + + test "edit without editor gives hint" do + run_edit_command("config/tokens.yml.enc", editor: "").tap do |output| + assert_match "No $EDITOR to open file in", output + assert_match "bin/rails encrypted:edit", output + end + end + + test "edit encrypted file" do + # Run twice to ensure file can be reread after first edit pass. + 2.times do + assert_match(/access_key_id: 123/, run_edit_command("config/tokens.yml.enc")) + end + end + + test "edit command does not add master key to gitignore when already exist" do + run_edit_command("config/tokens.yml.enc") + + Dir.chdir(app_path) do + assert_match "/config/master.key", File.read(".gitignore") + end + end + + test "edit encrypts file with custom key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + Dir.chdir(app_path) do + assert File.exist?("config/tokens.yml.enc") + assert File.exist?("config/tokens.key") + + assert_match "/config/tokens.key", File.read(".gitignore") + end + + assert_match(/access_key_id: 123/, run_edit_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + test "show encrypted file with custom key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + assert_match(/access_key_id: 123/, run_show_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + test "won't corrupt encrypted file when passed wrong key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + assert_match "passed the wrong key", + run_edit_command("config/tokens.yml.enc", allow_failure: true) + + assert_match(/access_key_id: 123/, run_show_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + private + def run_edit_command(file, key: nil, editor: "cat", **options) + switch_env("EDITOR", editor) do + rails "encrypted:edit", prepare_args(file, key), **options + end + end + + def run_show_command(file, key: nil) + rails "encrypted:show", prepare_args(file, key) + end + + def prepare_args(file, key) + args = [ file ] + args.push("--key", key) if key + args + end +end -- cgit v1.2.3 From 059d1edcbc6a556237fa31c6547f0faa11da328e Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Wed, 15 Nov 2017 22:00:54 +0100 Subject: Pass options onto key file generator. --- railties/lib/rails/generators/rails/master_key/master_key_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb index e57f07c1ae..de65146a8e 100644 --- a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb +++ b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb @@ -36,7 +36,7 @@ module Rails private def key_file_generator - EncryptionKeyFileGenerator.new + EncryptionKeyFileGenerator.new([], options) end def key_ignore -- cgit v1.2.3 From 376094caa44b1d641e3ed62466f085f6d8500999 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Wed, 15 Nov 2017 14:09:18 -0700 Subject: Bump resque-scheduler ahead of a new gem release --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 23034d7c26..4b6ed47945 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,7 +8,7 @@ GIT GIT remote: https://github.com/jeremy/resque-scheduler.git - revision: 7cccf7d8db4dbbf54bf549a98b6cbb38106dbed2 + revision: f5d56ec25ec20d939bf12d259be107f278de4044 branch: redis-rb-4.0 specs: resque-scheduler (4.3.0) @@ -229,7 +229,7 @@ GEM em-socksify (0.3.1) eventmachine (>= 1.0.0.beta.4) erubi (1.6.1) - et-orbi (1.0.5) + et-orbi (1.0.8) tzinfo event_emitter (0.2.6) eventmachine (1.2.5) -- cgit v1.2.3 From f27319a72a4ccfbffc575b752e4d91136f23725e Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 16 Nov 2017 07:41:14 +0900 Subject: Add master key to `gitignore` on `rails new` We generate master key on `rails new`. Therefore, if do not add master key to `.gitginore` on `rails new`as well, there is a possibility that the master key will be committed accidentally. --- railties/lib/rails/generators/rails/app/app_generator.rb | 4 +++- .../rails/encryption_key_file/encryption_key_file_generator.rb | 4 ++++ .../lib/rails/generators/rails/master_key/master_key_generator.rb | 4 ++++ railties/test/generators/app_generator_test.rb | 8 ++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index a99037576d..1fdfc3ca52 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -162,7 +162,9 @@ module Rails return if options[:pretend] || options[:dummy_app] require "rails/generators/rails/master_key/master_key_generator" - Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet]).add_master_key_file_silently + master_key_generator = Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet]) + master_key_generator.add_master_key_file_silently + master_key_generator.ignore_master_key_file_silently end def credentials diff --git a/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb index dd0d0c6c66..a396a9661f 100644 --- a/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +++ b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb @@ -44,6 +44,10 @@ module Rails end end + def ignore_key_file_silently(key_path, ignore: key_ignore(key_path)) + append_to_file ".gitignore", ignore if File.exist?(".gitignore") + end + private def key_ignore(key_path) [ "", "/#{key_path}", "" ].join("\n") diff --git a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb index de65146a8e..7f57340c11 100644 --- a/railties/lib/rails/generators/rails/master_key/master_key_generator.rb +++ b/railties/lib/rails/generators/rails/master_key/master_key_generator.rb @@ -34,6 +34,10 @@ module Rails key_file_generator.ignore_key_file(MASTER_KEY_PATH, ignore: key_ignore) end + def ignore_master_key_file_silently + key_file_generator.ignore_key_file_silently(MASTER_KEY_PATH, ignore: key_ignore) + end + private def key_file_generator EncryptionKeyFileGenerator.new([], options) diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 78962ee30b..fddfab172e 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -838,6 +838,14 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_equal 5, @sequence_step end + def test_gitignore + run_generator + + assert_file ".gitignore" do |content| + assert_match(/config\/master\.key/, content) + end + end + def test_system_tests_directory_generated run_generator -- cgit v1.2.3 From 23b9ad5fb103b0a50fbeede84279881d9dbc1687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Wn=C4=99trzak?= Date: Thu, 16 Nov 2017 09:52:51 +0100 Subject: Fixed example of `Rails.application.encrypted` method usage [ci skip] --- railties/lib/rails/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 31bc542308..df266fbfce 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -442,7 +442,7 @@ module Rails # the file with the master key. # The master key is either stored in `config/master.key` or `ENV["RAILS_MASTER_KEY"]`. # - # Rails.application.encrypted("config/mystery_man.key").read + # Rails.application.encrypted("config/mystery_man.txt.enc").read # # => "We've met before, haven't we?" # # It's also possible to interpret encrypted YAML files with `config`. -- cgit v1.2.3 From 4f7ab453e4afbf2302291d320967f38735de2e31 Mon Sep 17 00:00:00 2001 From: Matthew Draper Date: Thu, 29 Dec 2016 21:55:42 +1030 Subject: Start on a guide for the Executor & Load Interlock --- guides/source/documents.yaml | 5 + guides/source/threading_and_code_execution.md | 262 ++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 guides/source/threading_and_code_execution.md diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index 59205ee465..126d2e4845 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -159,6 +159,11 @@ url: engines.html description: This guide explains how to write a mountable engine. work_in_progress: true + - + name: Threading and Code Execution in Rails + url: threading_and_code_execution.html + description: This guide describes the considerations needed and tools available when working directly with concurrency in a Rails application. + work_in_progress: true - name: Contributing to Ruby on Rails documents: diff --git a/guides/source/threading_and_code_execution.md b/guides/source/threading_and_code_execution.md new file mode 100644 index 0000000000..b35b2fdee4 --- /dev/null +++ b/guides/source/threading_and_code_execution.md @@ -0,0 +1,262 @@ +**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.** + +Threading and Code Execution in Rails +===================================== + +After reading this guide, you will know: + +* What code Rails will automatically execute concurrently +* How to integrate manual concurrency with Rails internals +* How to wrap all application code +* How to affect application reloading + +-------------------------------------------------------------------------------- + +Automatic Concurrency +--------------------- + +Rails automatically allows various operations to be performed at the same time. + +When using a threaded web server, such as the default Puma, multiple HTTP +requests will be served simultaneously, with each request provided its own +controller instance. + +Threaded Active Job adapters, including the built-in Async, will likewise +execute several jobs at the same time. Action Cable channels are managed this +way too. + +These mechanisms all involve multiple threads, each managing work for a unique +instance of some object (controller, job, channel), while sharing the global +process space (such as classes and their configurations, and global variables). +As long as your code doesn't modify any of those shared things, it can mostly +ignore that other threads exist. + +The rest of this guide describes the mechanisms Rails uses to make it "mostly +ignorable", and how extensions and applications with special needs can use them. + +Executor +-------- + +The Rails Executor separates application code from framework code: any time the +framework invokes code you've written in your application, it will be wrapped by +the Executor. + +The Executor consists of two callbacks: `to_run` and `to_complete`. The Run +callback is called before the application code, and the Complete callback is +called after. + +### Default callbacks + +In a default Rails application, the Executor callbacks are used to: + +* track which threads are in safe positions for autoloading and reloading +* enable and disable the Active Record query cache +* return acquired Active Record connections to the pool +* constrain internal cache lifetimes + +### Wrapping application code + +If you're writing a library or component that will invoke application code, you +should wrap it with a call to the executor: + +```ruby +Rails.application.executor.wrap do + # call application code here +end +``` + +TIP: If you repeatedly invoke application code from a long-running process, you +may want to wrap using the Reloader instead. + +Each thread should be wrapped before it runs application code, so if your +application manually delegates work to other threads, such as via `Thread.new` +or Concurrent Ruby features that use thread pools, you should immediately wrap +the block: + +```ruby +Thread.new do + Rails.application.executor.wrap do + # your code here + end +end +``` + +NOTE: Concurrent Ruby uses a `ThreadPoolExecutor`, which it sometimes configures +with an `executor` option. Despite the name, it is unrelated. + +The Executor is safely re-entrant; if it is already active on the current +thread, `wrap` is a no-op. + +If it's impractical to physically wrap the application code in a block (for +example, the Rack API makes this problematic), you can also use the `run!` / +`complete!` pair. + +### Concurrency + +The Executor will put the current thread into `running` mode in the Load +Interlock. This operation will block temporarily if another thread is currently +either autoloading a constant or unloading/reloading the application. + +Reloader +-------- + +Like the Executor, the Reloader also wraps application code. If the Executor is +not already active on the current thread, the Reloader will invoke it for you, +so you only need to call one. This also guarantees that everything the Reloader +does, including all its callback invocations, occurs wrapped inside the +Executor. + +```ruby +Rails.application.reloader.wrap do + # call application code here +end +``` + +### Callbacks + +Before entering the wrapped block, the Reloader will check whether the running +application needs to be reloaded -- for example, because a model's source file has +been modified. If it determines a reload is required, it will wait until it's +safe, and then do so, before continuing. When the application is configured to +always reload regardless of whether any changes are detected, the reload is +instead performed at the end of the block. + +The Reloader also provides `to_run` and `to_complete` callbacks; they are +invoked at the same points as those of the Executor, but only when the current +execution has initiated an application reload. When no reload is deemed +necessary, the Reloader will invoke the wrapped block with no other callbacks. + +### Class Unload + +The most significant part of the reloading process is the Class Unload, where +all autoloaded classes are removed, ready to be loaded again. This will occur +immediately before either the Run or Complete callback, depending on the +`reload_classes_only_on_change` setting. + +Often, additional reloading actions need to be performed either just before or +just after the Class Unload, so the Reloader also provides `before_class_unload` +and `after_class_unload` callbacks. + +### Concurrency + +Only long-running "top level" processes should invoke the Reloader, because if +it determines a reload is needed, it will block until all other threads have +completed and left any Executor block. + +If this were to occur in a "child" thread, with a waiting parent inside the +Executor, it would cause an unavoidable deadlock: the reload must occur before +the child thread is executed, but it cannot be safely performed while the parent +thread is mid-execution. + +Child threads should use the Executor instead. + +Load Interlock +-------------- + +The Load Interlock allows autoloading and reloading to be enabled in a +multi-threaded runtime environment. + +When one thread is performing an autoload by evaluating the class definition +from the appropriate file, it is important no other thread encounters a +reference to the partially-defined constant. + +Similarly, it is only safe to perform an unload/reload when no application code +is in mid-execution: after the reload, the `User` constant, for example, may +point to a different class. Without this rule, a poorly-timed reload would mean +`User.new.class == User`, or even `User == User`, could be false. + +Both of these constraints are addressed by the Load Interlock. It keeps track of +which threads are currently running application code, loading a class, or +unloading autoloaded constants. + +Only one thread may load or unload at a time, and to do either, it must wait +until no other threads are running application code. If a thread is waiting to +perform a load, it doesn't prevent other threads from loading (in fact, they'll +cooperate, and each perform their queued load in turn, before all resuming +running together). + +### `permit_concurrent_loads` + +The Executor automatically acquires a `running` lock for the duration of its +block, and autoload knows when to upgrade to a `load` lock, and switch back to +`running` again afterwards. + +Other blocking operations performed inside the Executor block (which includes +all application code), however, can needlessly retain the `running` lock. If +another thread encounters a constant it must autoload, this can cause a +deadlock. + +For example, assuming `User` is not yet loaded, the following will deadlock: + +```ruby +Rails.application.executor.wrap do + th = Thread.new do + Rails.application.executor.wrap do + User # inner thread waits here; it cannot load + # User while another thread is running + end + end + + th.join # outer thread waits here, holding 'running' lock +end +``` + +To prevent this deadlock, the outer thread can `permit_concurrent_loads`. By +calling this method, the thread guarantees it will not dereference any +possibly-autoloaded constant inside the supplied block. The safest way to meet +that promise is to put it as close as possible to the blocking call only: + +```ruby +Rails.application.executor.wrap do + th = Thread.new do + Rails.application.executor.wrap do + User # inner thread can acquire the load lock, + # load User, and continue + end + end + + ActiveSupport::Dependencies.interlock.permit_concurrent_loads do + th.join # outer thread waits here, but has no lock + end +end +``` + +Another example, using Concurrent Ruby: + +```ruby +Rails.application.executor.wrap do + futures = 3.times.collect do |i| + Concurrent::Future.execute do + Rails.application.executor.wrap do + # do work here + end + end + end + + values = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do + futures.collect(&:value) + end +end +``` + + +### ActionDispatch::DebugLocks + +If your application is deadlocking and you think the Load Interlock may be +involved, you can temporarily add the ActionDispatch::DebugLocks middleware to +`config/application.rb`: + +```ruby +config.middleware.insert_before Rack::Sendfile, + ActionDispatch::DebugLocks +``` + +If you then restart the application and re-trigger the deadlock condition, +`/rails/locks` will show a summary of all threads currently known to the +interlock, which lock level they are holding or awaiting, and their current +backtrace. + +Generally a deadlock will be caused by the interlock conflicting with some other +external lock or blocking I/O call. Once you find it, you can wrap it with +`permit_concurrent_loads`. + -- cgit v1.2.3 From b5cd7d5b511361062f67ebad45ab38393970fc47 Mon Sep 17 00:00:00 2001 From: Matthew Draper Date: Fri, 17 Nov 2017 00:21:13 +1030 Subject: Add more detail on how the framework is actually configured by default --- guides/source/threading_and_code_execution.md | 72 +++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/guides/source/threading_and_code_execution.md b/guides/source/threading_and_code_execution.md index b35b2fdee4..1c7d61a29c 100644 --- a/guides/source/threading_and_code_execution.md +++ b/guides/source/threading_and_code_execution.md @@ -54,6 +54,12 @@ In a default Rails application, the Executor callbacks are used to: * return acquired Active Record connections to the pool * constrain internal cache lifetimes +Prior to Rails 5.0, some of these were handled by separate Rack middleware +classes (such as `ActiveRecord::ConnectionAdapters::ConnectionManagement`), or +directly wrapping code with methods like +`ActiveRecord::Base.connection_pool.with_connection do`. The Executor replaces +these with a single more abstract interface. + ### Wrapping application code If you're writing a library or component that will invoke application code, you @@ -89,7 +95,16 @@ thread, `wrap` is a no-op. If it's impractical to physically wrap the application code in a block (for example, the Rack API makes this problematic), you can also use the `run!` / -`complete!` pair. +`complete!` pair: + +```ruby +Thread.new do + execution_context = Rails.application.executor.run! + # your code here +ensure + execution_context.complete! if execution_context +end +``` ### Concurrency @@ -112,6 +127,12 @@ Rails.application.reloader.wrap do end ``` +The Reloader is only suitable where a long-running framework-level process +repeatedly calls into application code, such as for a web server or job queue. +Rails automatically wraps web requests and Active Job workers, so you'll rarely +need to invoke the Reloader for yourself. Always consider whether the Executor +is a better fit for your use case. + ### Callbacks Before entering the wrapped block, the Reloader will check whether the running @@ -141,14 +162,55 @@ and `after_class_unload` callbacks. Only long-running "top level" processes should invoke the Reloader, because if it determines a reload is needed, it will block until all other threads have -completed and left any Executor block. +completed any Executor invocations. If this were to occur in a "child" thread, with a waiting parent inside the Executor, it would cause an unavoidable deadlock: the reload must occur before the child thread is executed, but it cannot be safely performed while the parent -thread is mid-execution. - -Child threads should use the Executor instead. +thread is mid-execution. Child threads should use the Executor instead. + +Framework Behavior +------------------ + +The Rails framework components use these tools to manage their own concurrency +needs too. + +`ActionDispatch::Executor` and `ActionDispatch::Reloader` are Rack middlewares +that wraps the request with a supplied Executor or Reloader, respectively. They +are automatically included in the default application stack. The Reloader will +ensure any arriving HTTP request is served with a freshly-loaded copy of the +application if any code changes have occurred. + +Active Job also wraps its job executions with the Reloader, loading the latest +code to execute each job as it comes off the queue. + +Action Cable uses the Executor instead: because a Cable connection is linked to +a specific instance of a class, it's not possible to reload for every arriving +websocket message. Only the message handler is wrapped, though; a long-running +Cable connection does not prevent a reload that's triggered by a new incoming +request or job. Instead, Action Cable uses the Reloader's `before_class_unload` +callback to disconnect all its connections. When the client automatically +reconnects, it will be speaking to the new version of the code. + +The above are the entry points to the framework, so they are responsible for +ensuring their respective threads are protected, and deciding whether a reload +is necessary. Other components only need to use the Executor when they spawn +additional threads. + +### Configuration + +The Reloader only checks for file changes when `cache_classes` is false and +`reload_classes_only_on_change` is true (which is the default in the +`development` environment). + +When `cache_classes` is true (in `production`, by default), the Reloader is only +a pass-through to the Executor. + +The Executor always has important work to do, like database connection +management. When `cache_classes` and `eager_load` are both true (`production`), +no autoloading or class reloading will occur, so it does not need the Load +Interlock. If either of those are false (`development`), then the Executor will +use the Load Interlock to ensure constants are only loaded when it is safe. Load Interlock -------------- -- cgit v1.2.3 From bbae710a405ce92074c1666dcf859196c29e2ed2 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 17 Nov 2017 03:12:57 +0900 Subject: Avoid creating extra `relation` and `build_arel` in `_create_record` and `_update_record` (#29999) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently `_create_record` and `_update_record` in `Persistence` are creating extra `unscoped` and calling `build_arel` in the relation. But `compile_insert` and `compile_update` can be done without those expensive operation for `SelectManager` creation. So I moved the implementation to `Persistence` to avoid creating extra relation and refactored to avoid calling `build_arel`. https://gist.github.com/kamipo/8ed73d760112cfa5f6263c9413633419 Before: ``` Warming up -------------------------------------- _update_record 150.000 i/100ms Calculating ------------------------------------- _update_record 1.548k (±12.3%) i/s - 7.650k in 5.042603s ``` After: ``` Warming up -------------------------------------- _update_record 201.000 i/100ms Calculating ------------------------------------- _update_record 2.002k (±12.8%) i/s - 9.849k in 5.027681s ``` 30% faster for STI classes. --- activerecord/lib/active_record/persistence.rb | 43 ++++++++++++++++++- activerecord/lib/active_record/relation.rb | 61 --------------------------- 2 files changed, 41 insertions(+), 63 deletions(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index a57c60ffac..4e1b05dbf6 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -165,6 +165,38 @@ module ActiveRecord where(primary_key => id_or_array).delete_all end + def _insert_record(values) # :nodoc: + primary_key_value = nil + + if primary_key && Hash === values + arel_primary_key = arel_attribute(primary_key) + primary_key_value = values[arel_primary_key] + + if !primary_key_value && prefetch_primary_key? + primary_key_value = next_sequence_value + values[arel_primary_key] = primary_key_value + end + end + + if values.empty? + im = arel_table.compile_insert(connection.empty_insert_statement_value) + im.into arel_table + else + im = arel_table.compile_insert(_substitute_values(values)) + end + + connection.insert(im, "#{self} Create", primary_key || false, primary_key_value) + end + + def _update_record(values, id, id_was) # :nodoc: + bind = predicate_builder.build_bind_attribute(primary_key, id_was || id) + um = arel_table.where( + arel_attribute(primary_key).eq(bind) + ).compile_update(_substitute_values(values), primary_key) + + connection.update(um, "#{self} Update") + end + private # Called by +instantiate+ to decide which class to use for a new # record instance. @@ -174,6 +206,13 @@ module ActiveRecord def discriminate_class_for_record(record) self end + + def _substitute_values(values) + values.map do |attr, value| + bind = predicate_builder.build_bind_attribute(attr.name, value) + [attr, bind] + end + end end # Returns true if this object hasn't been saved yet -- that is, a record @@ -671,7 +710,7 @@ module ActiveRecord rows_affected = 0 @_trigger_update_callback = true else - rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database + rows_affected = self.class._update_record(attributes_values, id, id_in_database) @_trigger_update_callback = rows_affected > 0 end @@ -685,7 +724,7 @@ module ActiveRecord def _create_record(attribute_names = self.attribute_names) attributes_values = arel_attributes_with_values_for_create(attribute_names) - new_id = self.class.unscoped.insert attributes_values + new_id = self.class._insert_record(attributes_values) self.id ||= new_id if self.class.primary_key @new_record = false diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index e2d2f45503..081ef5771f 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -36,67 +36,6 @@ module ActiveRecord reset end - def insert(values) # :nodoc: - primary_key_value = nil - - if primary_key && Hash === values - primary_key_value = values[values.keys.find { |k| - k.name == primary_key - }] - - if !primary_key_value && klass.prefetch_primary_key? - primary_key_value = klass.next_sequence_value - values[arel_attribute(klass.primary_key)] = primary_key_value - end - end - - im = arel.create_insert - im.into @table - - substitutes = substitute_values values - - if values.empty? # empty insert - im.values = Arel.sql(connection.empty_insert_statement_value) - else - im.insert substitutes - end - - @klass.connection.insert( - im, - "#{@klass} Create", - primary_key || false, - primary_key_value, - nil, - ) - end - - def _update_record(values, id, id_was) # :nodoc: - substitutes = substitute_values values - - scope = @klass.unscoped - - if @klass.finder_needs_type_condition? - scope.unscope!(where: @klass.inheritance_column) - end - - relation = scope.where(@klass.primary_key => (id_was || id)) - um = relation - .arel - .compile_update(substitutes, @klass.primary_key) - - @klass.connection.update( - um, - "#{@klass} Update", - ) - end - - def substitute_values(values) # :nodoc: - values.map do |arel_attr, value| - bind = predicate_builder.build_bind_attribute(arel_attr.name, value) - [arel_attr, bind] - end - end - def arel_attribute(name) # :nodoc: klass.arel_attribute(name, table) end -- cgit v1.2.3 From 83cb835ff70044d4a0cf0b56acf9503161f8ba8c Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Thu, 16 Nov 2017 21:13:51 +0300 Subject: Fix links [ci skip] --- guides/source/security.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/source/security.md b/guides/source/security.md index cfa777d433..fa90cadcd2 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -192,9 +192,9 @@ rotations going at any one time. For more details on key rotation with encrypted and signed messages as well as the various options the `rotate` method accepts, please refer to the -[MessageEncryptor API](api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html) +[MessageEncryptor API](http://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html) and -[MessageVerifier API](api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html) +[MessageVerifier API](http://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html) documentation. ### Replay Attacks for CookieStore Sessions -- cgit v1.2.3 From 3bbc7f8f635b0d89e546c90d1b9db0683beece75 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 16 Nov 2017 11:31:26 -0700 Subject: Move back to resque-scheduler mainline now that https://github.com/resque/resque-scheduler/pull/620 is merged --- Gemfile | 2 +- Gemfile.lock | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index 9ff34dc26c..b20e477764 100644 --- a/Gemfile +++ b/Gemfile @@ -55,7 +55,7 @@ gem "bootsnap", ">= 1.1.0", require: false # Active Job. group :job do gem "resque", require: false - gem "resque-scheduler", github: "jeremy/resque-scheduler", branch: "redis-rb-4.0", require: false + gem "resque-scheduler", github: "resque/resque-scheduler", require: false gem "sidekiq", require: false gem "sucker_punch", require: false gem "delayed_job", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 4b6ed47945..8c678fa660 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,17 +6,6 @@ GIT queue_classic (3.2.0.RC1) pg (>= 0.17, < 0.20) -GIT - remote: https://github.com/jeremy/resque-scheduler.git - revision: f5d56ec25ec20d939bf12d259be107f278de4044 - branch: redis-rb-4.0 - specs: - resque-scheduler (4.3.0) - mono_logger (~> 1.0) - redis (>= 3.3, < 5) - resque (~> 1.26) - rufus-scheduler (~> 3.2) - GIT remote: https://github.com/matthewd/rb-inotify.git revision: 856730aad4b285969e8dd621e44808a7c5af4242 @@ -34,6 +23,16 @@ GIT event_emitter websocket +GIT + remote: https://github.com/resque/resque-scheduler.git + revision: 284b862dd63967da1cf3a7e6abd9d2052067c8be + specs: + resque-scheduler (4.3.0) + mono_logger (~> 1.0) + redis (>= 3.3, < 5) + resque (~> 1.26) + rufus-scheduler (~> 3.2) + GIT remote: https://github.com/robin850/sdoc.git revision: 0e340352f3ab2f196c8a8743f83c2ee286e4f71c -- cgit v1.2.3 From f32cff5563f2188e657aa2fd9f8513f0da4a49ca Mon Sep 17 00:00:00 2001 From: Matthew Draper Date: Fri, 17 Nov 2017 21:55:39 +1030 Subject: Improve AR connection fork safety Use whatever adapter-provided means we have available to ensure forked children don't send quit/shutdown/goodbye messages to the server on connections that belonged to their parent. --- .../abstract/connection_pool.rb | 35 ++++++++++++++++++++++ .../connection_adapters/abstract_adapter.rb | 13 ++++++++ .../connection_adapters/mysql2_adapter.rb | 5 ++++ .../connection_adapters/postgresql_adapter.rb | 5 ++++ .../connection_adapters/connection_handler_test.rb | 32 ++++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 0759f4d2b3..0f39c077c9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -450,6 +450,21 @@ module ActiveRecord disconnect(false) end + # Discards all connections in the pool (even if they're currently + # leased!), along with the pool itself. Any further interaction with the + # pool (except #spec and #schema_cache) is undefined. + # + # See AbstractAdapter#discard! + def discard! # :nodoc: + synchronize do + return if @connections.nil? # already discarded + @connections.each do |conn| + conn.discard! + end + @connections = @available = @thread_cached_conns = nil + end + end + # Clears the cache which maps classes and re-connects connections that # require reloading. # @@ -866,11 +881,31 @@ module ActiveRecord # about the model. The model needs to pass a specification name to the handler, # in order to look up the correct connection pool. class ConnectionHandler + def self.unowned_pool_finalizer(pid_map) # :nodoc: + lambda do |_| + discard_unowned_pools(pid_map) + end + end + + def self.discard_unowned_pools(pid_map) # :nodoc: + pid_map.each do |pid, pools| + pools.values.compact.each(&:discard!) unless pid == Process.pid + end + end + def initialize # These caches are keyed by spec.name (ConnectionSpecification#name). @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k| + # Discard the parent's connection pools immediately; we have no need + # of them + ConnectionHandler.discard_unowned_pools(h) + h[k] = Concurrent::Map.new(initial_capacity: 2) end + + # Backup finalizer: if the forked child never needed a pool, the above + # early discard has not occurred + ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool) end def connection_pool_list diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 345983a655..df11391fa7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -366,6 +366,19 @@ module ActiveRecord reset_transaction end + # Immediately forget this connection ever existed. Unlike disconnect!, + # this will not communicate with the server. + # + # After calling this method, the behavior of all other methods becomes + # undefined. This is called internally just before a forked process gets + # rid of a connection that belonged to its parent. + def discard! + # This should be overridden by concrete adapters. + # + # Prevent @connection's finalizer from touching the socket, or + # otherwise communicating with its server, when it is collected. + end + # Reset the state of this connection, directing the DBMS to clear # transactions and other connection-related server-side state. Usually a # database-dependent operation. diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 8de582fee1..1d614dc8bf 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -105,6 +105,11 @@ module ActiveRecord @connection.close end + def discard! # :nodoc: + @connection.automatic_close = false + @connection = nil + end + private def connect diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 5ce6765dd8..1739c288b6 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -273,6 +273,11 @@ module ActiveRecord end end + def discard! # :nodoc: + @connection.socket_io.reopen(IO::NULL) + @connection = nil + end + def native_database_types #:nodoc: NATIVE_DATABASE_TYPES end diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 74d0ed348e..cae74a2b9b 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -1,10 +1,15 @@ # frozen_string_literal: true require "cases/helper" +require "models/person" module ActiveRecord module ConnectionAdapters class ConnectionHandlerTest < ActiveRecord::TestCase + self.use_transactional_tests = false + + fixtures :people + def setup @handler = ConnectionHandler.new @spec_name = "primary" @@ -139,6 +144,33 @@ module ActiveRecord rd.close end + def test_forked_child_doesnt_mangle_parent_connection + object_id = ActiveRecord::Base.connection.object_id + assert ActiveRecord::Base.connection.active? + + rd, wr = IO.pipe + rd.binmode + wr.binmode + + pid = fork { + rd.close + if ActiveRecord::Base.connection.active? + wr.write Marshal.dump ActiveRecord::Base.connection.object_id + end + wr.close + + exit # allow finalizers to run + } + + wr.close + + Process.waitpid pid + assert_not_equal object_id, Marshal.load(rd.read) + rd.close + + assert_equal 3, ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM people") + end + def test_retrieve_connection_pool_copies_schema_cache_from_ancestor_pool @pool.schema_cache = @pool.connection.schema_cache @pool.schema_cache.add("posts") -- cgit v1.2.3 From 60c550370a0a915d7a74e1c128d3b233ea5aca7b Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Tue, 13 Jun 2017 07:44:49 +0900 Subject: Deprecate an `after_bundle` callback in Rails plugin templates Since fbd1e98cf983572ca9884f17f933ffe92833632a, Rails plugin does not run `bundle install` when generating. Therefore, `after_bundle` callback is not actually executed after `bundle`. Since there is a difference between the name and the actual behavior, I think that should be remove. --- railties/CHANGELOG.md | 4 +++ .../generators/rails/plugin/plugin_generator.rb | 4 +++ railties/test/generators/plugin_generator_test.rb | 32 ++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index e69a1231b1..d086248278 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,7 @@ +* Deprecate `after_bundle` callback in Rails plugin templates. + + *Yuji Yaginuma* + * `rails new` and `rails plugin new` get `Active Storage` by default. Add ability to skip `Active Storage` with `--skip-active-storage` and do so automatically when `--skip-active-record` is used. diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index 786aea503c..a83c911806 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -263,6 +263,10 @@ task default: :test public_task :apply_rails_template def run_after_bundle_callbacks + unless @after_bundle_callbacks.empty? + ActiveSupport::Deprecation.warn("`after_bundle` is deprecated and will be removed in the next version of Rails. ") + end + @after_bundle_callbacks.each do |callback| callback.call end diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 06f59ee33d..fc7584c175 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -721,6 +721,38 @@ class PluginGeneratorTest < Rails::Generators::TestCase Object.send(:remove_const, "ENGINE_ROOT") end + def test_after_bundle_callback + path = "http://example.org/rails_template" + template = %{ after_bundle { run "echo ran after_bundle" } }.dup + template.instance_eval "def read; self; end" # Make the string respond to read + + check_open = -> *args do + assert_equal [ path, "Accept" => "application/x-thor-template" ], args + template + end + + sequence = ["echo ran after_bundle"] + @sequence_step ||= 0 + ensure_bundler_first = -> command do + assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" + @sequence_step += 1 + end + + content = nil + generator([destination_root], template: path).stub(:open, check_open, template) do + generator.stub(:bundle_command, ensure_bundler_first) do + generator.stub(:run, ensure_bundler_first) do + silence_stream($stdout) do + content = capture(:stderr) { generator.invoke_all } + end + end + end + end + + assert_equal 1, @sequence_step + assert_match(/DEPRECATION WARNING: `after_bundle` is deprecated/, content) + end + private def action(*args, &block) -- cgit v1.2.3 From 067fc779c4560fff4812614a2f78f9248f3e55f8 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 18 Nov 2017 15:52:40 +0900 Subject: Fix "warning: assigned but unused variable - key" Ref: https://travis-ci.org/rails/rails/jobs/303840778#L1974 --- activesupport/lib/active_support/cache/redis_cache_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb index 358de9203d..2cbeaef6fa 100644 --- a/activesupport/lib/active_support/cache/redis_cache_store.rb +++ b/activesupport/lib/active_support/cache/redis_cache_store.rb @@ -303,7 +303,7 @@ module ActiveSupport keys_to_names = names.map { |name| [ normalize_key(name, options), name ] }.to_h values = redis.mget(*keys_to_names.keys) - keys_to_names.zip(values).each_with_object({}) do |((key, name), value), results| + keys_to_names.zip(values).each_with_object({}) do |((_, name), value), results| if value entry = deserialize_entry(value) unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options)) -- cgit v1.2.3 From 5accc6304085c0e415658897a07d3a4c22d9d39e Mon Sep 17 00:00:00 2001 From: Pierre Hedkvist Date: Thu, 16 Nov 2017 14:29:15 +0000 Subject: [ci skip] Added example for using headless_chrome with ActionDispatch::SystemTestCase --- guides/source/testing.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/guides/source/testing.md b/guides/source/testing.md index c5b2a694e7..8416fd163d 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -671,6 +671,16 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase end ``` +If you want to use a headless browser, you could use Headless Chrome by adding `headless_chrome` in the `:using` argument. + +```ruby +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :headless_chrome +end +``` + If your Capybara configuration requires more setup than provided by Rails, this additional configuration could be added into the `application_system_test_case.rb` file. -- cgit v1.2.3 From 83cb0fc6326b308322e35b211bac31c73b346b73 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 19 Nov 2017 14:42:45 +0900 Subject: Fix formatting of `credentials` and `encrypted` [ci skip] --- railties/lib/rails/application.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index df266fbfce..b1429df18b 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -429,23 +429,23 @@ module Rails end end - # Decrypts the credentials hash as kept in `config/credentials.yml.enc`. This file is encrypted with - # the Rails master key, which is either taken from ENV["RAILS_MASTER_KEY"] or from loading - # `config/master.key`. + # Decrypts the credentials hash as kept in +config/credentials.yml.enc+. This file is encrypted with + # the Rails master key, which is either taken from ENV["RAILS_MASTER_KEY"] or from loading + # +config/master.key+. def credentials @credentials ||= encrypted("config/credentials.yml.enc") end # Shorthand to decrypt any encrypted configurations or files. # - # For any file added with `bin/rails encrypted:edit` call `read` to decrypt + # For any file added with bin/rails encrypted:edit call +read+ to decrypt # the file with the master key. - # The master key is either stored in `config/master.key` or `ENV["RAILS_MASTER_KEY"]`. + # The master key is either stored in +config/master.key+ or ENV["RAILS_MASTER_KEY"]. # # Rails.application.encrypted("config/mystery_man.txt.enc").read # # => "We've met before, haven't we?" # - # It's also possible to interpret encrypted YAML files with `config`. + # It's also possible to interpret encrypted YAML files with +config+. # # Rails.application.encrypted("config/credentials.yml.enc").config # # => { next_guys_line: "I don't think so. Where was it you think we met?" } @@ -456,11 +456,11 @@ module Rails # # => "I don't think so. Where was it you think we met?" # # The files or configs can also be encrypted with a custom key. To decrypt with - # a key in the `ENV`, use: + # a key in the +ENV+, use: # # Rails.application.encrypted("config/special_tokens.yml.enc", env_key: "SPECIAL_TOKENS") # - # Or to decrypt with a file, that should be version control ignored, relative to `Rails.root`: + # Or to decrypt with a file, that should be version control ignored, relative to +Rails.root+: # # Rails.application.encrypted("config/special_tokens.yml.enc", key_path: "config/special_tokens.key") def encrypted(path, key_path: "config/master.key", env_key: "RAILS_MASTER_KEY") -- cgit v1.2.3 From 125095c2d5845044dc214e136c3a758b4ed205e3 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 20 Nov 2017 01:55:03 +0900 Subject: Generate `keys` instead of `keys_to_names` `keys_to_names` is used only for `keys_to_names.keys`. --- activesupport/lib/active_support/cache/redis_cache_store.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb index 2cbeaef6fa..08200a556f 100644 --- a/activesupport/lib/active_support/cache/redis_cache_store.rb +++ b/activesupport/lib/active_support/cache/redis_cache_store.rb @@ -300,10 +300,10 @@ module ActiveSupport options = names.extract_options! options = merged_options(options) - keys_to_names = names.map { |name| [ normalize_key(name, options), name ] }.to_h - values = redis.mget(*keys_to_names.keys) + keys = names.map { |name| normalize_key(name, options) } + values = redis.mget(*keys) - keys_to_names.zip(values).each_with_object({}) do |((_, name), value), results| + names.zip(values).each_with_object({}) do |(name, value), results| if value entry = deserialize_entry(value) unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options)) -- cgit v1.2.3 From 1997c9a13730cc82e3c4d42ab2995452cfa21074 Mon Sep 17 00:00:00 2001 From: Roman Kovtunenko Date: Sun, 19 Nov 2017 21:54:30 +0200 Subject: Fix names of http authentication modules in api_app guides --- guides/source/api_app.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/guides/source/api_app.md b/guides/source/api_app.md index 43a7de88b0..b360f270d7 100644 --- a/guides/source/api_app.md +++ b/guides/source/api_app.md @@ -414,8 +414,10 @@ Some common modules you might want to add: - `AbstractController::Translation`: Support for the `l` and `t` localization and translation methods. -- `ActionController::HttpAuthentication::Basic` (or `Digest` or `Token`): Support - for basic, digest or token HTTP authentication. +- Support for basic, digest or token HTTP authentication: + * `ActionController::HttpAuthentication::Basic::ControllerMethods`, + * `ActionController::HttpAuthentication::Digest::ControllerMethods`, + * `ActionController::HttpAuthentication::Token::ControllerMethods` - `ActionView::Layouts`: Support for layouts when rendering. - `ActionController::MimeResponds`: Support for `respond_to`. - `ActionController::Cookies`: Support for `cookies`, which includes -- cgit v1.2.3 From 1a0f85e13963f0765dad8b378b651ccece051e2c Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 20 Nov 2017 05:11:12 +0900 Subject: Fix ASt CI failure with rack-test 0.7.1 Due to https://github.com/rack-test/rack-test/commit/5fd3631078e7c73aaed7d4371f70fb2a79384be9. --- activestorage/test/models/attachments_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index e645d868ce..23533ece1e 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -27,7 +27,7 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase test "attach new blob from an UploadedFile" do file = file_fixture "racecar.jpg" - @user.avatar.attach Rack::Test::UploadedFile.new file + @user.avatar.attach Rack::Test::UploadedFile.new file.to_s assert_equal "racecar.jpg", @user.avatar.filename.to_s end -- cgit v1.2.3 From bc2769774808cbc33b90dc8156ea53542cbde6f6 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 20 Nov 2017 05:49:51 +0900 Subject: Fix `test_session_store_with_expire_after` failure with rack-test 0.7.1 https://travis-ci.org/rails/rails/jobs/304428814#L1977 --- Gemfile.lock | 2 +- actionpack/test/dispatch/session/cookie_store_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8c678fa660..f02dfa8f60 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -371,7 +371,7 @@ GEM rack (>= 0.4) rack-protection (2.0.0) rack - rack-test (0.7.0) + rack-test (0.7.1) rack (>= 1.0, < 3) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index cf51c47068..2c43a8c29f 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -283,8 +283,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest with_test_route_set(expire_after: 5.hours) do # First request accesses the session time = Time.local(2008, 4, 24) - cookie_body = nil - Time.stub :now, time do expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") @@ -303,6 +301,8 @@ class CookieStoreTest < ActionDispatch::IntegrationTest Time.stub :now, time do expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") + cookies[SessionKey] = SignedBar + get "/no_session_access" assert_response :success -- cgit v1.2.3 From eeaf9cf61c3cd14929583878785c31dab79e2196 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 20 Nov 2017 06:24:50 +0900 Subject: Prevent extra `spawn` to make `klass.all` faster (#29009) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These extra `spawn` are called via `klass.all` and `klass.all` is called everywhere in the internal. Avoiding the extra `spawn` makes` klass.all` 30% faster for STI classes. https://gist.github.com/kamipo/684d03817a8115848cec8e8b079560b7 ``` Warming up -------------------------------------- fast relation 4.410k i/100ms slow relation 3.334k i/100ms Calculating ------------------------------------- fast relation 47.373k (± 5.2%) i/s - 238.140k in 5.041836s slow relation 35.757k (±15.9%) i/s - 176.702k in 5.104625s Comparison: fast relation: 47373.2 i/s slow relation: 35756.7 i/s - 1.32x slower ``` --- activerecord/lib/active_record/core.rb | 3 ++- activerecord/lib/active_record/scoping/default.rb | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index b97b14644e..481159e501 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -277,7 +277,8 @@ module ActiveRecord relation = Relation.create(self, arel_table, predicate_builder) if finder_needs_type_condition? && !ignore_default_scope? - relation.where(type_condition).create_with(inheritance_column.to_s => sti_name) + relation.where!(type_condition) + relation.create_with!(inheritance_column.to_s => sti_name) else relation end diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index 86ae374318..8c612df27a 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -111,7 +111,7 @@ module ActiveRecord # The user has defined their own default scope method, so call that evaluate_default_scope do if scope = default_scope - (base_rel ||= relation).merge(scope) + (base_rel ||= relation).merge!(scope) end end elsif default_scopes.any? @@ -119,7 +119,7 @@ module ActiveRecord evaluate_default_scope do default_scopes.inject(base_rel) do |default_scope, scope| scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call) - default_scope.merge(base_rel.instance_exec(&scope)) + default_scope.merge!(base_rel.instance_exec(&scope)) end end end -- cgit v1.2.3 From e05e2ae44f1ecf8e9bb5949f531305c15bc3c665 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Sun, 19 Nov 2017 17:34:07 -0500 Subject: Permit attaching files to new records Closes #31164. --- activestorage/lib/active_storage/attached/many.rb | 6 +++- activestorage/lib/active_storage/attached/one.rb | 8 ++--- activestorage/test/models/attachments_test.rb | 44 +++++++++++++++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/activestorage/lib/active_storage/attached/many.rb b/activestorage/lib/active_storage/attached/many.rb index 0b3400bccf..6eace65b79 100644 --- a/activestorage/lib/active_storage/attached/many.rb +++ b/activestorage/lib/active_storage/attached/many.rb @@ -20,7 +20,11 @@ module ActiveStorage # document.images.attach([ first_blob, second_blob ]) def attach(*attachables) attachables.flatten.collect do |attachable| - attachments.create!(name: name, blob: create_blob_from(attachable)) + if record.new_record? + attachments.build(record: record, blob: create_blob_from(attachable)) + else + attachments.create!(record: record, blob: create_blob_from(attachable)) + end end end diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb index 7092f6b109..0244232b2c 100644 --- a/activestorage/lib/active_storage/attached/one.rb +++ b/activestorage/lib/active_storage/attached/one.rb @@ -23,7 +23,7 @@ module ActiveStorage if attached? && dependent == :purge_later replace attachable else - write_attachment create_attachment_from(attachable) + write_attachment build_attachment_from(attachable) end end @@ -67,13 +67,13 @@ module ActiveStorage blob.tap do transaction do detach - write_attachment create_attachment_from(attachable) + write_attachment build_attachment_from(attachable) end end.purge_later end - def create_attachment_from(attachable) - ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable)) + def build_attachment_from(attachable) + ActiveStorage::Attachment.new(record: record, name: name, blob: create_blob_from(attachable)) end def write_attachment(attachment) diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index 23533ece1e..edd68d3051 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -56,6 +56,26 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert ActiveStorage::Blob.service.exist?(@user.avatar.key) end + test "attach blob to new record" do + user = User.new(name: "Jason") + + assert_no_changes -> { user.new_record? } do + assert_no_difference -> { ActiveStorage::Attachment.count } do + user.avatar.attach create_blob(filename: "funky.jpg") + end + end + + assert user.avatar.attached? + assert_equal "funky.jpg", user.avatar.filename.to_s + + assert_difference -> { ActiveStorage::Attachment.count }, +1 do + user.save! + end + + assert user.reload.avatar.attached? + assert_equal "funky.jpg", user.avatar.filename.to_s + end + test "access underlying associations of new blob" do @user.avatar.attach create_blob(filename: "funky.jpg") assert_equal @user, @user.avatar_attachment.record @@ -160,6 +180,30 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "country.jpg", @user.highlights.second.filename.to_s end + test "attach blobs to new record" do + user = User.new(name: "Jason") + + assert_no_changes -> { user.new_record? } do + assert_no_difference -> { ActiveStorage::Attachment.count } do + user.highlights.attach( + { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" }, + { io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" }) + end + end + + assert user.highlights.attached? + assert_equal "town.jpg", user.highlights.first.filename.to_s + assert_equal "country.jpg", user.highlights.second.filename.to_s + + assert_difference -> { ActiveStorage::Attachment.count }, +2 do + user.save! + end + + assert user.reload.highlights.attached? + assert_equal "town.jpg", user.highlights.first.filename.to_s + assert_equal "country.jpg", user.highlights.second.filename.to_s + end + test "find attached blobs" do @user.highlights.attach( { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" }, -- cgit v1.2.3 From 00b3d9f52f965cad6e653ceb8f5c21a96742c55a Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Mon, 20 Nov 2017 19:19:00 +0530 Subject: Pass over Execution guide[ci skip] --- guides/source/threading_and_code_execution.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guides/source/threading_and_code_execution.md b/guides/source/threading_and_code_execution.md index 1c7d61a29c..3d3d31b97e 100644 --- a/guides/source/threading_and_code_execution.md +++ b/guides/source/threading_and_code_execution.md @@ -57,7 +57,7 @@ In a default Rails application, the Executor callbacks are used to: Prior to Rails 5.0, some of these were handled by separate Rack middleware classes (such as `ActiveRecord::ConnectionAdapters::ConnectionManagement`), or directly wrapping code with methods like -`ActiveRecord::Base.connection_pool.with_connection do`. The Executor replaces +`ActiveRecord::Base.connection_pool.with_connection`. The Executor replaces these with a single more abstract interface. ### Wrapping application code @@ -93,7 +93,7 @@ with an `executor` option. Despite the name, it is unrelated. The Executor is safely re-entrant; if it is already active on the current thread, `wrap` is a no-op. -If it's impractical to physically wrap the application code in a block (for +If it's impractical to wrap the application code in a block (for example, the Rack API makes this problematic), you can also use the `run!` / `complete!` pair: @@ -266,7 +266,7 @@ end To prevent this deadlock, the outer thread can `permit_concurrent_loads`. By calling this method, the thread guarantees it will not dereference any possibly-autoloaded constant inside the supplied block. The safest way to meet -that promise is to put it as close as possible to the blocking call only: +that promise is to put it as close as possible to the blocking call: ```ruby Rails.application.executor.wrap do -- cgit v1.2.3 From 2d20a7696a761b1840bc2fbe09a2fd4bff2a779f Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Mon, 20 Nov 2017 10:52:54 -0500 Subject: Fix direct uploads to local service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable CSRF protection for ActiveStorage::DiskController#update. The local disk service is intended to imitate a third-party service like S3 or GCS, so we don't care where direct uploads originate: they’re authorized by signed tokens. Closes #30290. [Shinichi Maeshima & George Claghorn] --- activestorage/app/controllers/active_storage/disk_controller.rb | 2 ++ activestorage/test/dummy/config/environments/test.rb | 3 +++ activestorage/test/test_helper.rb | 1 + 3 files changed, 6 insertions(+) diff --git a/activestorage/app/controllers/active_storage/disk_controller.rb b/activestorage/app/controllers/active_storage/disk_controller.rb index a4fd427cb2..8caecfff49 100644 --- a/activestorage/app/controllers/active_storage/disk_controller.rb +++ b/activestorage/app/controllers/active_storage/disk_controller.rb @@ -5,6 +5,8 @@ # Always go through the BlobsController, or your own authenticated controller, rather than directly # to the service url. class ActiveStorage::DiskController < ActionController::Base + skip_forgery_protection + def show if key = decode_verified_key send_data disk_service.download(key), diff --git a/activestorage/test/dummy/config/environments/test.rb b/activestorage/test/dummy/config/environments/test.rb index ce0889e8ae..74a802d98c 100644 --- a/activestorage/test/dummy/config/environments/test.rb +++ b/activestorage/test/dummy/config/environments/test.rb @@ -30,6 +30,9 @@ Rails.application.configure do # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + # Raises error for missing translations # config.action_view.raise_on_missing_translations = true end diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb index 55da781f2a..aaf1d452ea 100644 --- a/activestorage/test/test_helper.rb +++ b/activestorage/test/test_helper.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +ENV["RAILS_ENV"] ||= "test" require_relative "dummy/config/environment.rb" require "bundler/setup" -- cgit v1.2.3 From ae7593e7e842ec5efb8d58a7ce005ba55fd4c886 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Mon, 20 Nov 2017 10:57:29 -0500 Subject: Load 5.2 defaults in ASt dummy app --- activestorage/test/dummy/config/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/test/dummy/config/application.rb b/activestorage/test/dummy/config/application.rb index 7ee6625bb5..06cbc453a2 100644 --- a/activestorage/test/dummy/config/application.rb +++ b/activestorage/test/dummy/config/application.rb @@ -19,7 +19,7 @@ Bundler.require(*Rails.groups) module Dummy class Application < Rails::Application - config.load_defaults 5.1 + config.load_defaults 5.2 config.active_storage.service = :local end -- cgit v1.2.3 From b22ee64b5b30c6d5039c292235e10b24b1057f6d Mon Sep 17 00:00:00 2001 From: Takumasa Ochi Date: Sun, 19 Nov 2017 03:50:59 +0900 Subject: MemCacheStore: Support expiring counters Support `expires_in` in `ActiveSupport::Cache::MemCacheStore#increment` and `#decrement`. Closes #30716. --- activesupport/CHANGELOG.md | 13 +++++++++++++ .../lib/active_support/cache/mem_cache_store.rb | 4 ++-- activesupport/test/cache/stores/mem_cache_store_test.rb | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 904dab0e05..88bbafc3a8 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,16 @@ +* MemCacheStore: Support expiring counters. + + Pass `expires_in: [seconds]` to `#increment` and `#decrement` options + to set the Memcached TTL (time-to-live) if the counter doesn't exist. + If the counter exists, Memcached doesn't extend its expiry when it's + incremented or decremented. + + ``` + Rails.cache.increment("my_counter", 1, expires_in: 2.minutes) + ``` + + *Takumasa Ochi* + * Handle `TZInfo::AmbiguousTime` errors Make `ActiveSupport::TimeWithZone` match Ruby's handling of ambiguous diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 9242a334f2..50f072388d 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -122,7 +122,7 @@ module ActiveSupport options = merged_options(options) instrument(:increment, name, amount: amount) do rescue_error_with nil do - @data.incr(normalize_key(name, options), amount) + @data.incr(normalize_key(name, options), amount, options[:expires_in]) end end end @@ -135,7 +135,7 @@ module ActiveSupport options = merged_options(options) instrument(:decrement, name, amount: amount) do rescue_error_with nil do - @data.decr(normalize_key(name, options), amount) + @data.decr(normalize_key(name, options), amount, options[:expires_in]) end end end diff --git a/activesupport/test/cache/stores/mem_cache_store_test.rb b/activesupport/test/cache/stores/mem_cache_store_test.rb index 1b73fb65eb..99624caf8a 100644 --- a/activesupport/test/cache/stores/mem_cache_store_test.rb +++ b/activesupport/test/cache/stores/mem_cache_store_test.rb @@ -57,6 +57,22 @@ class MemCacheStoreTest < ActiveSupport::TestCase end end + def test_increment_expires_in + cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true) + cache.clear + assert_called_with cache.instance_variable_get(:@data), :incr, [ "foo", 1, 60 ] do + cache.increment("foo", 1, expires_in: 60) + end + end + + def test_decrement_expires_in + cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true) + cache.clear + assert_called_with cache.instance_variable_get(:@data), :decr, [ "foo", 1, 60 ] do + cache.decrement("foo", 1, expires_in: 60) + end + end + def test_local_cache_raw_values_with_marshal cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true) cache.clear -- cgit v1.2.3 From 1d24e47140356f136471d15e3ce3fa427f4430c2 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Mon, 20 Nov 2017 18:06:06 -0500 Subject: Provide attachment writers Permit creating a record and attaching files in a single step. # Before: User.create!(user_params.except(:avatar)).tap do |user| user.avatar.attach(user_params[:avatar]) end # After: User.create!(user_params) [Yoshiyuki Hirano & George Claghorn] --- .../lib/active_storage/attached/macros.rb | 8 ++++++ activestorage/test/models/attachments_test.rb | 32 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb index f0256718ac..2b38a9b887 100644 --- a/activestorage/lib/active_storage/attached/macros.rb +++ b/activestorage/lib/active_storage/attached/macros.rb @@ -32,6 +32,10 @@ module ActiveStorage def #{name} @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self, dependent: #{dependent == :purge_later ? ":purge_later" : "false"}) end + + def #{name}=(attachable) + #{name}.attach(attachable) + end CODE has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record @@ -73,6 +77,10 @@ module ActiveStorage def #{name} @active_storage_attached_#{name} ||= ActiveStorage::Attached::Many.new("#{name}", self, dependent: #{dependent == :purge_later ? ":purge_later" : "false"}) end + + def #{name}=(attachables) + #{name}.attach(attachables) + end CODE has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment" diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index edd68d3051..20eec3c220 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -76,6 +76,20 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "funky.jpg", user.avatar.filename.to_s end + test "build new record with attached blob" do + assert_no_difference -> { ActiveStorage::Attachment.count } do + @user = User.new(name: "Jason", avatar: { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" }) + end + + assert @user.new_record? + assert @user.avatar.attached? + assert_equal "town.jpg", @user.avatar.filename.to_s + + @user.save! + assert @user.reload.avatar.attached? + assert_equal "town.jpg", @user.avatar.filename.to_s + end + test "access underlying associations of new blob" do @user.avatar.attach create_blob(filename: "funky.jpg") assert_equal @user, @user.avatar_attachment.record @@ -204,6 +218,24 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "country.jpg", user.highlights.second.filename.to_s end + test "build new record with attached blobs" do + assert_no_difference -> { ActiveStorage::Attachment.count } do + @user = User.new(name: "Jason", highlights: [ + { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" }, + { io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" }]) + end + + assert @user.new_record? + assert @user.highlights.attached? + assert_equal "town.jpg", @user.highlights.first.filename.to_s + assert_equal "country.jpg", @user.highlights.second.filename.to_s + + @user.save! + assert @user.reload.highlights.attached? + assert_equal "town.jpg", @user.highlights.first.filename.to_s + assert_equal "country.jpg", @user.highlights.second.filename.to_s + end + test "find attached blobs" do @user.highlights.attach( { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" }, -- cgit v1.2.3 From adf7fb86d03339dbfde59aacd555b1b0c19b76f7 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Tue, 21 Nov 2017 09:31:57 +0900 Subject: Remove unused `em-hiredis` `em-hiredis` is unused since 48766e32d31651606b9f68a16015ad05c3b0de2c --- Gemfile | 1 - Gemfile.lock | 7 ------- 2 files changed, 8 deletions(-) diff --git a/Gemfile b/Gemfile index b20e477764..4a7f5210d3 100644 --- a/Gemfile +++ b/Gemfile @@ -74,7 +74,6 @@ end group :cable do gem "puma", require: false - gem "em-hiredis", require: false gem "hiredis", require: false gem "redis", "~> 4.0", require: false diff --git a/Gemfile.lock b/Gemfile.lock index f02dfa8f60..d5c12c81bb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -216,9 +216,6 @@ GEM activerecord (>= 3.0, < 5.2) delayed_job (>= 3.0, < 5) digest-crc (0.4.1) - em-hiredis (0.3.1) - eventmachine (~> 1.0) - hiredis (~> 0.6.0) em-http-request (1.1.5) addressable (>= 2.3.4) cookiejar (!= 0.3.1) @@ -232,9 +229,6 @@ GEM tzinfo event_emitter (0.2.6) eventmachine (1.2.5) - eventmachine (1.2.5-java) - eventmachine (1.2.5-x64-mingw32) - eventmachine (1.2.5-x86-mingw32) execjs (2.7.0) faraday (0.13.1) multipart-post (>= 1.2, < 3) @@ -523,7 +517,6 @@ DEPENDENCIES dalli (>= 2.2.1) delayed_job delayed_job_active_record - em-hiredis google-cloud-storage (~> 1.3) hiredis json (>= 2.0.0) -- cgit v1.2.3 From a163f9328b2b45ff7699f372c6d714466b6e85c9 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 21 Nov 2017 11:52:57 +0800 Subject: Update `rack-test` to 0.8. --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index d5c12c81bb..075f8c2b0e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -365,7 +365,7 @@ GEM rack (>= 0.4) rack-protection (2.0.0) rack - rack-test (0.7.1) + rack-test (0.8.0) rack (>= 1.0, < 3) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) -- cgit v1.2.3 From 2cc5ccedb1cabd818ece3d6736b412de086a16c1 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Tue, 21 Nov 2017 17:43:42 +0000 Subject: No difference between JRuby and CRuby at test_read_attributes_before_type_cast_on_a_boolean https://github.com/jruby/activerecord-jdbc-adapter ActiveRecord JDBC Adapter is actively developed and it supports Rails 5.1 now. This pull request addresses one of the failure when running ActiveRecord unit tests with ActiveRecord JDBC Adapter. As of right now, ActiveRecord JDBC Adapter supports Rails 5.1, not master branch then this test only can run on `5-1-stable` branch. But I have opened this pull request to `master` branch since this type cast should be going to work in the future versions of ActiveRecord JDBC Adapter . ```ruby $ ARCONN=jdbcmysql bin/test test/cases/attribute_methods_test.rb:203 Using jdbcmysql Run options: --seed 8874 F Finished in 0.709120s, 1.4102 runs/s, 1.4102 assertions/s. 1) Failure: AttributeMethodsTest#test_read_attributes_before_type_cast_on_a_boolean [/home/yahonda/git/rails/activerecord/test/cases/attribute_methods_test.rb:203]: Expected: "0" Actual: 0 1 runs, 1 assertions, 1 failures, 0 errors, 0 skips $ ``` --- activerecord/test/cases/attribute_methods_test.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 2f42684212..c48f7d3518 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -200,12 +200,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase if current_adapter?(:Mysql2Adapter) test "read attributes_before_type_cast on a boolean" do bool = Boolean.create!("value" => false) - if RUBY_PLATFORM.include?("java") - # JRuby will return the value before typecast as string. - assert_equal "0", bool.reload.attributes_before_type_cast["value"] - else - assert_equal 0, bool.reload.attributes_before_type_cast["value"] - end + assert_equal 0, bool.reload.attributes_before_type_cast["value"] end end -- cgit v1.2.3 From 516c63a71e632c862841cbcb3f9b7ecfc05d9675 Mon Sep 17 00:00:00 2001 From: Joe Marty Date: Tue, 21 Nov 2017 12:27:25 -0600 Subject: Fix tld_length documentation Change recommendation for tld_length (for sharing cookies across subdomains of a 2-token TLD), to 2 instead of 1. --- actionpack/lib/action_dispatch/middleware/cookies.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 86a070c6ad..ea4156c972 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -161,7 +161,7 @@ module ActionDispatch # # * :tld_length - When using :domain => :all, this option can be used to explicitly # set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD. - # For example, to share cookies between user1.lvh.me and user2.lvh.me, set :tld_length to 1. + # For example, to share cookies between user1.lvh.me and user2.lvh.me, set :tld_length to 2. # * :expires - The time at which this cookie expires, as a \Time or ActiveSupport::Duration object. # * :secure - Whether this cookie is only transmitted to HTTPS servers. # Default is +false+. -- cgit v1.2.3 From 3fa812615a28f9c6392c433f3b08c41c5efb999f Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Tue, 21 Nov 2017 14:17:12 -0500 Subject: Fix that some ASt route helpers silently discarded options --- activestorage/config/routes.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/activestorage/config/routes.rb b/activestorage/config/routes.rb index c659e079fd..1eae21445a 100644 --- a/activestorage/config/routes.rb +++ b/activestorage/config/routes.rb @@ -3,38 +3,38 @@ Rails.application.routes.draw do get "/rails/active_storage/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob, internal: true - direct :rails_blob do |blob| - route_for(:rails_service_blob, blob.signed_id, blob.filename) + direct :rails_blob do |blob, options| + route_for(:rails_service_blob, blob.signed_id, blob.filename, options) end - resolve("ActiveStorage::Blob") { |blob| route_for(:rails_blob, blob) } - resolve("ActiveStorage::Attachment") { |attachment| route_for(:rails_blob, attachment.blob) } + resolve("ActiveStorage::Blob") { |blob, options| route_for(:rails_blob, blob) } + resolve("ActiveStorage::Attachment") { |attachment, options| route_for(:rails_blob, attachment.blob, options) } get "/rails/active_storage/variants/:signed_blob_id/:variation_key/*filename" => "active_storage/variants#show", as: :rails_blob_variation, internal: true - direct :rails_variant do |variant| + direct :rails_variant do |variant, options| signed_blob_id = variant.blob.signed_id variation_key = variant.variation.key filename = variant.blob.filename - route_for(:rails_blob_variation, signed_blob_id, variation_key, filename) + route_for(:rails_blob_variation, signed_blob_id, variation_key, filename, options) end - resolve("ActiveStorage::Variant") { |variant| route_for(:rails_variant, variant) } + resolve("ActiveStorage::Variant") { |variant, options| route_for(:rails_variant, variant, options) } get "/rails/active_storage/previews/:signed_blob_id/:variation_key/*filename" => "active_storage/previews#show", as: :rails_blob_preview, internal: true - direct :rails_preview do |preview| + direct :rails_preview do |preview, options| signed_blob_id = preview.blob.signed_id variation_key = preview.variation.key filename = preview.blob.filename - route_for(:rails_blob_preview, signed_blob_id, variation_key, filename) + route_for(:rails_blob_preview, signed_blob_id, variation_key, filename, options) end - resolve("ActiveStorage::Preview") { |preview| route_for(:rails_preview, preview) } + resolve("ActiveStorage::Preview") { |preview, options| route_for(:rails_preview, preview, options) } get "/rails/active_storage/disk/:encoded_key/*filename" => "active_storage/disk#show", as: :rails_disk_service, internal: true -- cgit v1.2.3 From 4d5f0bb30b5ac76407c9864b83b69b8a83ac3dd6 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Tue, 21 Nov 2017 14:59:30 -0500 Subject: Fix loading ActiveStorage::DiskController when CSRF protection is disabled by default --- activestorage/app/controllers/active_storage/disk_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/app/controllers/active_storage/disk_controller.rb b/activestorage/app/controllers/active_storage/disk_controller.rb index 8caecfff49..a7e10c0696 100644 --- a/activestorage/app/controllers/active_storage/disk_controller.rb +++ b/activestorage/app/controllers/active_storage/disk_controller.rb @@ -5,7 +5,7 @@ # Always go through the BlobsController, or your own authenticated controller, rather than directly # to the service url. class ActiveStorage::DiskController < ActionController::Base - skip_forgery_protection + skip_forgery_protection if default_protect_from_forgery def show if key = decode_verified_key -- cgit v1.2.3 From 424debb096c670404428141b13de2af0185c9675 Mon Sep 17 00:00:00 2001 From: willnet Date: Sat, 22 Jul 2017 00:01:40 +0900 Subject: =?UTF-8?q?Fix=20generator=20suggestion=20raise=20error=20when=20I?= =?UTF-8?q?18n.available=5Flocales=20don=E2=80=99t=20include=20:en?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- railties/lib/rails/generators.rb | 3 ++- railties/test/generators_test.rb | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 5592e8d78e..6c9c109f17 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -274,8 +274,9 @@ module Rails else options = sorted_groups.flat_map(&:last) suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3) + suggestions.map! { |s| "'#{s}'" } msg = "Could not find generator '#{namespace}'. ".dup - msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n" + msg << "Maybe you meant #{ suggestions[0...-1].join(', ')} or #{suggestions[-1]}\n" msg << "Run `rails generate --help` for more options." puts msg end diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 28e7617d7f..1735804664 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -36,6 +36,19 @@ class GeneratorsTest < Rails::Generators::TestCase assert_match "Maybe you meant 'migration'", output end + def test_generator_suggestions_except_en_locale + orig_available_locales = I18n.available_locales + orig_default_locale = I18n.default_locale + I18n.available_locales = :ja + I18n.default_locale = :ja + name = :tas + output = capture(:stdout) { Rails::Generators.invoke name } + assert_match "Maybe you meant 'task', 'job' or", output + ensure + I18n.available_locales = orig_available_locales + I18n.default_locale = orig_default_locale + end + def test_generator_multiple_suggestions name = :tas output = capture(:stdout) { Rails::Generators.invoke name } -- cgit v1.2.3 From d5eb8f23c8543ae3c8cff6aefdbb6bbcf2f28128 Mon Sep 17 00:00:00 2001 From: Francis Go Date: Wed, 22 Nov 2017 16:33:28 +0000 Subject: Update Action Cable Overview Guide [ci skip] --- guides/source/action_cable_overview.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md index 57403a4bf9..1a86b1fcbb 100644 --- a/guides/source/action_cable_overview.md +++ b/guides/source/action_cable_overview.md @@ -1,12 +1,12 @@ Action Cable Overview ===================== -In this guide you will learn how Action Cable works and how to use WebSockets to +In this guide, you will learn how Action Cable works and how to use WebSockets to incorporate real-time features into your Rails application. After reading this guide, you will know: -* What Action Cable is and its integration on backend and frontend +* What Action Cable is and its integration backend and frontend * How to setup Action Cable * How to setup channels * Deployment and Architecture setup for running Action Cable @@ -129,7 +129,7 @@ subscriptions based on an identifier sent by the cable consumer. # app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel # Called when the consumer has successfully - # become a subscriber of this channel. + # become a subscriber to this channel. def subscribed end end @@ -225,7 +225,7 @@ A *broadcasting* is a pub/sub link where anything transmitted by a publisher is routed directly to the channel subscribers who are streaming that named broadcasting. Each channel can be streaming zero or more broadcastings. -Broadcastings are purely an online queue and time dependent. If a consumer is +Broadcastings are purely an online queue and time-dependent. If a consumer is not streaming (subscribed to a given channel), they'll not get the broadcast should they connect later. @@ -515,8 +515,8 @@ user. For a user with an ID of 1, the broadcasting name would be The channel has been instructed to stream everything that arrives at `web_notifications:1` directly to the client by invoking the `received` callback. The data passed as argument is the hash sent as the second parameter -to the server-side broadcast call, JSON encoded for the trip across the wire, -and unpacked for the data argument arriving to `received`. +to the server-side broadcast call, JSON encoded for the trip across the wire +and unpacked for the data argument arriving as `received`. ### More Complete Examples @@ -569,7 +569,7 @@ This may change in the future. [#27214](https://github.com/rails/rails/issues/27 Action Cable will only accept requests from specified origins, which are passed to the server config as an array. The origins can be instances of -strings or regular expressions, against which a check for match will be performed. +strings or regular expressions, against which a check for the match will be performed. ```ruby config.action_cable.allowed_request_origins = ['http://rubyonrails.com', %r{http://ruby.*}] @@ -592,7 +592,7 @@ environment configuration files. ### Other Configurations -The other common option to configure, is the log tags applied to the +The other common option to configure is the log tags applied to the per-connection logger. Here's an example that uses the user account id if available, else "no-account" while tagging: @@ -607,7 +607,7 @@ config.action_cable.log_tags = [ For a full list of all configuration options, see the `ActionCable::Server::Configuration` class. -Also note that your server must provide at least the same number of database +Also, note that your server must provide at least the same number of database connections as you have workers. The default worker pool size is set to 4, so that means you have to make at least that available. You can change that in `config/database.yml` through the `pool` attribute. -- cgit v1.2.3 From 078421bacba178eac6a8e607b16f3f4511c5d72f Mon Sep 17 00:00:00 2001 From: Chris LaRose Date: Wed, 22 Nov 2017 09:12:08 -0800 Subject: Make ActiveSupport::TimeZone.all independent of previous lookups (#31176) --- activesupport/CHANGELOG.md | 7 +++++++ activesupport/lib/active_support/values/time_zone.rb | 12 +++++++++--- activesupport/test/time_zone_test.rb | 7 +++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 88bbafc3a8..3257c63fd2 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,10 @@ +* Make `ActiveSupport::TimeZone.all` return only time zones that are in + `ActiveSupport::TimeZone::MAPPING`. + + Fixes #7245. + + *Chris LaRose* + * MemCacheStore: Support expiring counters. Pass `expires_in: [seconds]` to `#increment` and `#decrement` options diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index b294d99fe0..07e37f5dd2 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -256,6 +256,13 @@ module ActiveSupport @country_zones[code] ||= load_country_zones(code) end + def clear() #:nodoc: + @lazy_zones_map = Concurrent::Map.new + @country_zones = Concurrent::Map.new + @zones = nil + @zones_map = nil + end + private def load_country_zones(code) country = TZInfo::Country.get(code) @@ -269,9 +276,8 @@ module ActiveSupport end def zones_map - @zones_map ||= begin - MAPPING.each_key { |place| self[place] } # load all the zones - @lazy_zones_map + @zones_map ||= MAPPING.each_with_object({}) do |(name, _), zones| + zones[name] = self[name] end end end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 862e872494..405c8f315b 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -718,6 +718,13 @@ class TimeZoneTest < ActiveSupport::TestCase end end + def test_all_uninfluenced_by_time_zone_lookups_delegated_to_tzinfo + ActiveSupport::TimeZone.clear + galapagos = ActiveSupport::TimeZone["Pacific/Galapagos"] + all_zones = ActiveSupport::TimeZone.all + assert_not_includes all_zones, galapagos + end + def test_index assert_nil ActiveSupport::TimeZone["bogus"] assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone["Central Time (US & Canada)"] -- cgit v1.2.3 From 3063ace1070e4ddb8d0cc09fbd23049e7b21617a Mon Sep 17 00:00:00 2001 From: "T.J. Schuck" Date: Wed, 22 Nov 2017 14:45:51 -0500 Subject: Update incorrect backtick usage in RDoc to teletype [ci skip] --- actionpack/lib/action_controller/metal/redirecting.rb | 4 ++-- .../lib/action_controller/metal/request_forgery_protection.rb | 6 +++--- actionpack/lib/action_dispatch/routing/url_for.rb | 2 +- activerecord/lib/active_record/migration.rb | 4 ++-- activesupport/lib/active_support/cache/redis_cache_store.rb | 10 +++++----- activesupport/lib/active_support/core_ext/array/access.rb | 4 ++-- .../lib/active_support/core_ext/date_time/compatibility.rb | 4 ++-- .../lib/active_support/core_ext/object/with_options.rb | 2 +- .../lib/active_support/core_ext/string/inflections.rb | 2 +- .../lib/active_support/core_ext/string/output_safety.rb | 2 +- activesupport/lib/active_support/inflector/transliterate.rb | 4 ++-- activesupport/lib/active_support/message_encryptor.rb | 4 ++-- activesupport/lib/active_support/message_verifier.rb | 6 +++--- 13 files changed, 27 insertions(+), 27 deletions(-) diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 8de57f9199..87a2e29a3f 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -68,7 +68,7 @@ module ActionController # if possible, otherwise redirects to the provided default fallback # location. # - # The referrer information is pulled from the HTTP `Referer` (sic) header on + # The referrer information is pulled from the HTTP +Referer+ (sic) header on # the request. This is an optional header and its presence on the request is # subject to browser security settings and user preferences. If the request # is missing this header, the fallback_location will be used. @@ -82,7 +82,7 @@ module ActionController # redirect_back fallback_location: '/', allow_other_host: false # # ==== Options - # * :fallback_location - The default fallback location that will be used on missing `Referer` header. + # * :fallback_location - The default fallback location that will be used on missing +Referer+ header. # * :allow_other_host - Allows or disallow redirection to the host that is different to the current host # # All other options that can be passed to redirect_to are accepted as diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index bd133f24a1..906494ba16 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -216,7 +216,7 @@ module ActionController #:nodoc: # 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 - # `), javascript_include_tag("foo.js") + end + def test_image_path ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end @@ -778,6 +791,23 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase end end +class AssetTagHelperWithoutRequestTest < ActionView::TestCase + tests ActionView::Helpers::AssetTagHelper + + undef :request + + def test_stylesheet_link_tag_without_request + assert_dom_equal( + %(), + stylesheet_link_tag("foo.css") + ) + end + + def test_javascript_include_tag_without_request + assert_dom_equal %(), javascript_include_tag("foo.js") + end +end + class AssetUrlHelperControllerTest < ActionView::TestCase tests ActionView::Helpers::AssetUrlHelper -- cgit v1.2.3 From 333ff24b8524094d55badb6c7f42f4b53832c0b6 Mon Sep 17 00:00:00 2001 From: "T.J. Schuck" Date: Tue, 28 Nov 2017 17:49:54 -0500 Subject: Refactor Date/Time next_occurring and prev_occurring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These methods were originally added in https://github.com/rails/rails/pull/26600 This includes a couple of refactors to make these methods behave more similarly to other Date/Time extensions added by Active Support: 1. Use `advance` instead of `since` and `ago` to time-travel — this is particularly important to keep the returned instance’s class matching `self`. Before this change: today = Date.today # => Tue, 28 Nov 2017 today.class # => Date today.next_occurring(:wednesday) # => Wed, 29 Nov 2017 00:00:00 UTC +00:00 today.next_occurring(:wednesday).class # => ActiveSupport::TimeWithZone After this change, a Date (or Time, or DateTime) instance is properly returned (just like is shown in the new docs). This is generally how everything else in DateAndTime::Calculations works. 2. Move the tests from the DateTime tests to the DateAndTimeBehavior tests. The latter location is mixed in to the core_ext tests for _all_ of Date, Time, and DateTime to test the behavior across all of the classes. The previous location is for testing core_ext functionality added specifically just to DateTime. 3. Better docs! --- .../core_ext/date_and_time/calculations.rb | 16 ++++++++++++---- .../test/core_ext/date_and_time_behavior.rb | 20 ++++++++++++++++++++ activesupport/test/core_ext/date_time_ext_test.rb | 22 ---------------------- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index 061b79e098..f6cb1a384c 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -330,20 +330,28 @@ module DateAndTime beginning_of_year..end_of_year end - # Returns specific next occurring day of week + # Returns a new date/time representing the next occurrence of the specified day of week. + # + # today = Date.today # => Thu, 14 Dec 2017 + # today.next_occurring(:monday) # => Mon, 18 Dec 2017 + # today.next_occurring(:thursday) # => Thu, 21 Dec 2017 def next_occurring(day_of_week) current_day_number = wday != 0 ? wday - 1 : 6 from_now = DAYS_INTO_WEEK.fetch(day_of_week) - current_day_number from_now += 7 unless from_now > 0 - since(from_now.days) + advance(days: from_now) end - # Returns specific previous occurring day of week + # Returns a new date/time representing the previous occurrence of the specified day of week. + # + # today = Date.today # => Thu, 14 Dec 2017 + # today.prev_occurring(:monday) # => Mon, 11 Dec 2017 + # today.prev_occurring(:thursday) # => Thu, 07 Dec 2017 def prev_occurring(day_of_week) current_day_number = wday != 0 ? wday - 1 : 6 ago = current_day_number - DAYS_INTO_WEEK.fetch(day_of_week) ago += 7 unless ago > 0 - ago(ago.days) + advance(days: -ago) end private diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb index 42da6f6cd0..91b92043d0 100644 --- a/activesupport/test/core_ext/date_and_time_behavior.rb +++ b/activesupport/test/core_ext/date_and_time_behavior.rb @@ -328,6 +328,26 @@ module DateAndTimeBehavior assert_equal date_time_init(2007, 12, 31, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 12, 31, 10, 10, 10).end_of_year end + def test_next_occurring + assert_equal date_time_init(2017, 12, 18, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).next_occurring(:monday) + assert_equal date_time_init(2017, 12, 19, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).next_occurring(:tuesday) + assert_equal date_time_init(2017, 12, 20, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).next_occurring(:wednesday) + assert_equal date_time_init(2017, 12, 21, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).next_occurring(:thursday) + assert_equal date_time_init(2017, 12, 15, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).next_occurring(:friday) + assert_equal date_time_init(2017, 12, 16, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).next_occurring(:saturday) + assert_equal date_time_init(2017, 12, 17, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).next_occurring(:sunday) + end + + def test_prev_occurring + assert_equal date_time_init(2017, 12, 11, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).prev_occurring(:monday) + assert_equal date_time_init(2017, 12, 12, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).prev_occurring(:tuesday) + assert_equal date_time_init(2017, 12, 13, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).prev_occurring(:wednesday) + assert_equal date_time_init(2017, 12, 7, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).prev_occurring(:thursday) + assert_equal date_time_init(2017, 12, 8, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).prev_occurring(:friday) + assert_equal date_time_init(2017, 12, 9, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).prev_occurring(:saturday) + assert_equal date_time_init(2017, 12, 10, 3, 14, 15), date_time_init(2017, 12, 14, 3, 14, 15).prev_occurring(:sunday) + end + def test_monday_with_default_beginning_of_week_set with_bw_default(:saturday) do assert_equal date_time_init(2012, 9, 17, 0, 0, 0), date_time_init(2012, 9, 18, 0, 0, 0).monday diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index d942cddb2a..ed962803fa 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -30,28 +30,6 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase end end - def test_next_occur - datetime = DateTime.new(2016, 9, 24, 0, 0) # saturday - assert_equal datetime.next_occurring(:monday), datetime.since(2.days) - assert_equal datetime.next_occurring(:tuesday), datetime.since(3.days) - assert_equal datetime.next_occurring(:wednesday), datetime.since(4.days) - assert_equal datetime.next_occurring(:thursday), datetime.since(5.days) - assert_equal datetime.next_occurring(:friday), datetime.since(6.days) - assert_equal datetime.next_occurring(:saturday), datetime.since(1.week) - assert_equal datetime.next_occurring(:sunday), datetime.since(1.day) - end - - def test_prev_occur - datetime = DateTime.new(2016, 9, 24, 0, 0) # saturday - assert_equal datetime.prev_occurring(:monday), datetime.ago(5.days) - assert_equal datetime.prev_occurring(:tuesday), datetime.ago(4.days) - assert_equal datetime.prev_occurring(:wednesday), datetime.ago(3.days) - assert_equal datetime.prev_occurring(:thursday), datetime.ago(2.days) - assert_equal datetime.prev_occurring(:friday), datetime.ago(1.day) - assert_equal datetime.prev_occurring(:saturday), datetime.ago(1.week) - assert_equal datetime.prev_occurring(:sunday), datetime.ago(6.days) - end - def test_readable_inspect datetime = DateTime.new(2005, 2, 21, 14, 30, 0) assert_equal "Mon, 21 Feb 2005 14:30:00 +0000", datetime.readable_inspect -- cgit v1.2.3 From 6815ce0be7febfea9b45643fb6b5e77e610bf0e6 Mon Sep 17 00:00:00 2001 From: "T.J. Schuck" Date: Tue, 28 Nov 2017 18:16:54 -0500 Subject: Formatting fix for example code Just cleaning up the formatting of the example code here to format an inline bit of commentary as a comment. Before: ![](https://monosnap.com/file/Clso8IQGOWHU3o6cStbY5NaMPx3ysP.png) After: ![](https://monosnap.com/file/QdbKKvLAeiQ0RucppVYETvaWEstnOI.png) [ci skip] --- activesupport/lib/active_support/deprecation/constant_accessor.rb | 2 +- activesupport/lib/active_support/deprecation/proxy_wrappers.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/deprecation/constant_accessor.rb b/activesupport/lib/active_support/deprecation/constant_accessor.rb index dd515cd6f4..1ed0015812 100644 --- a/activesupport/lib/active_support/deprecation/constant_accessor.rb +++ b/activesupport/lib/active_support/deprecation/constant_accessor.rb @@ -15,7 +15,7 @@ module ActiveSupport # # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto) # - # (In a later update, the original implementation of `PLANETS` has been removed.) + # # (In a later update, the original implementation of `PLANETS` has been removed.) # # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune) # include ActiveSupport::Deprecation::DeprecatedConstantAccessor diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 782ad2519c..896c0d2d8e 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -113,7 +113,7 @@ module ActiveSupport # # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto) # - # (In a later update, the original implementation of `PLANETS` has been removed.) + # # (In a later update, the original implementation of `PLANETS` has been removed.) # # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune) # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006') -- cgit v1.2.3 From eb90b8bc86e758045a707cae43d11dab538ca6db Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Tue, 28 Nov 2017 00:58:55 -0500 Subject: Add preload_link_tag helper. This helper creates a link tag with preload keyword that allows to browser to initiate early fetch of resources. Additionally this send Early Hints if supported. See https://github.com/rails/rails/pull/30744/commits/59a02fb7bcbe68f26e1e7fdcec45c00c66e4a065 for more details about Early Hints. Preload spec: https://w3c.github.io/preload/ --- actionview/CHANGELOG.md | 8 +++ .../lib/action_view/helpers/asset_tag_helper.rb | 75 ++++++++++++++++++++++ actionview/test/template/asset_tag_helper_test.rb | 15 +++++ 3 files changed, 98 insertions(+) diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 125f7f7555..566e30993b 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,11 @@ +* Add `preload_link_tag` helper + + This helper that allows to the browser to initiate early fetch of resources + (different to the specified in javascript_include_tag and stylesheet_link_tag). + Additionally, this sends Early Hints if supported by browser. + + *Guillermo Iguaran* + ## Rails 5.2.0.beta2 (November 28, 2017) ## * No changes. diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index f37f2ee0ff..da630129cb 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -2,6 +2,8 @@ require "active_support/core_ext/array/extract_options" require "active_support/core_ext/hash/keys" +require "active_support/core_ext/object/inclusion" +require "active_support/core_ext/object/try" require "action_view/helpers/asset_url_helper" require "action_view/helpers/tag_helper" @@ -221,6 +223,67 @@ module ActionView }.merge!(options.symbolize_keys)) end + # Returns a link tag that browsers can use to preload the +source+. + # The +source+ can be the path of an resource managed by asset pipeline, + # a full path or an URI. + # + # ==== Options + # + # * :type - Override the auto-generated mime type, defaults to the mime type for +source+ extension. + # * :as - Override the auto-generated value for as attribute, calculated using +source+ extension and mime type. + # * :crossorigin - Specify the crossorigin attribute, required to load cross-origin resources. + # * :nopush - Specify if the use of server push is not desired for the resource. Defaults to +false+. + # + # ==== Examples + # + # preload_link_tag("custom_theme.css") + # # => + # + # preload_link_tag("/videos/video.webm") + # # => + # + # preload_link_tag(post_path(format: :json), as: "fetch") + # # => + # + # preload_link_tag("worker.js", as: "worker") + # # => + # + # preload_link_tag("//example.com/font.woff2") + # # => + # + # preload_link_tag("//example.com/font.woff2", crossorigin: "use-credentials") + # # => + # + # preload_link_tag("/media/audio.ogg", nopush: true) + # # => + # + def preload_link_tag(source, options = {}) + href = asset_path(source, skip_pipeline: options.delete(:skip_pipeline)) + extname = File.extname(source).downcase.delete(".") + mime_type = options.delete(:type) || Template::Types[extname].try(:to_s) + as_type = options.delete(:as) || resolve_link_as(extname, mime_type) + crossorigin = options.delete(:crossorigin) + crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font") + nopush = options.delete(:nopush) || false + + link_tag = tag.link({ + rel: "preload", + href: href, + as: as_type, + type: mime_type, + crossorigin: crossorigin + }.merge!(options.symbolize_keys)) + + early_hints_link = "<#{href}>; rel=preload; as=#{as_type}" + early_hints_link += "; type=#{mime_type}" if mime_type + early_hints_link += "; crossorigin=#{crossorigin}" if crossorigin + early_hints_link += "; nopush" if nopush + + request.send_early_hints("Link" => early_hints_link) if respond_to?(:request) && request + + link_tag + end + # Returns an HTML image tag for the +source+. The +source+ can be a full # path, a file or an Active Storage attachment. # @@ -417,6 +480,18 @@ module ActionView raise ArgumentError, "Cannot pass a :size option with a :height or :width option" end end + + def resolve_link_as(extname, mime_type) + if extname == "js" + "script" + elsif extname == "css" + "style" + elsif extname == "vtt" + "track" + elsif (type = mime_type.to_s.split("/")[0]) && type.in?(%w(audio video font)) + type + end + end end end end diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index a8b66191bc..284dacf2d4 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -214,6 +214,17 @@ class AssetTagHelperTest < ActionView::TestCase %(favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png') => %() } + PreloadLinkToTag = { + %(preload_link_tag '/styles/custom_theme.css') => %(), + %(preload_link_tag '/videos/video.webm') => %(), + %(preload_link_tag '/posts.json', as: 'fetch') => %(), + %(preload_link_tag '/users', as: 'fetch', type: 'application/json') => %(), + %(preload_link_tag '//example.com/map?callback=initMap', as: 'fetch', type: 'application/javascript') => %(), + %(preload_link_tag '//example.com/font.woff2') => %(), + %(preload_link_tag '//example.com/font.woff2', crossorigin: 'use-credentials') => %(), + %(preload_link_tag '/media/audio.ogg', nopush: true) => %() + } + VideoPathToTag = { %(video_path("xml")) => %(/videos/xml), %(video_path("xml.ogg")) => %(/videos/xml.ogg), @@ -535,6 +546,10 @@ class AssetTagHelperTest < ActionView::TestCase FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end + def test_preload_link_tag + PreloadLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } + end + def test_video_path VideoPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end -- cgit v1.2.3 From 729a3da0bb5993a4464ebdebcce8be3635b7f765 Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Tue, 28 Nov 2017 01:09:06 -0500 Subject: Register most popular audio/video/font mime types supported by modern browsers --- actionpack/CHANGELOG.md | 4 ++++ actionpack/lib/action_dispatch/http/mime_types.rb | 20 +++++++++++++++++++- actionpack/test/dispatch/mime_type_test.rb | 12 ++++++------ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 2232767c05..c8fb34ed52 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Register most popular audio/video/font mime types supported by modern browsers. + + *Guillermo Iguaran* + * Fix optimized url helpers when using relative url root Fixes #31220. diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index f8e6fca36d..9f8db397fd 100644 --- a/actionpack/lib/action_dispatch/http/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb @@ -10,6 +10,7 @@ Mime::Type.register "text/css", :css Mime::Type.register "text/calendar", :ics Mime::Type.register "text/csv", :csv Mime::Type.register "text/vcard", :vcf +Mime::Type.register "text/vtt", :vtt, %w(vtt) Mime::Type.register "image/png", :png, [], %w(png) Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg) @@ -17,8 +18,25 @@ Mime::Type.register "image/gif", :gif, [], %w(gif) Mime::Type.register "image/bmp", :bmp, [], %w(bmp) Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff) Mime::Type.register "image/svg+xml", :svg +Mime::Type.register "image/webp", :webp, [], %w(webp) -Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe) +Mime::Type.register "audio/mpeg", :mpg, [], %w(mp1 mp2 mp3) +Mime::Type.register "audio/webm", :weba, [], %w(weba) +Mime::Type.register "audio/ogg", :ogg, [], %w(oga ogg spx opus) +Mime::Type.register "audio/aac", :acc, [], %w(aac) +Mime::Type.register "audio/mp4", :mp4, [], %w(m4a mpg4) +Mime::Type.register "audio/flac", :flac, [], %w(flac) + +Mime::Type.register "video/webm", :webm, [], %w(webm) +Mime::Type.register "video/mp4", :mp4, [], %w(mp4 m4v) +Mime::Type.register "video/ogg", :ogv, [], %w(ogv) + +Mime::Type.register "application/ogx", :ogx, [], %w(ogx) + +Mime::Type.register "font/otf", :otf, [], %w(otf) +Mime::Type.register "font/ttf", :ttf, [], %w(ttf) +Mime::Type.register "font/woff", :woff, [], %w(woff) +Mime::Type.register "font/woff2", :woff2, [], %w(woff2) Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml ) Mime::Type.register "application/rss+xml", :rss diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 90e95e972d..c0b4034d28 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -30,28 +30,28 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse text with trailing star at the beginning" do accept = "text/*, text/html, application/json, multipart/form-data" - expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml], Mime[:json], Mime[:multipart_form]] + expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml], Mime[:json], Mime[:multipart_form]] parsed = Mime::Type.parse(accept) - assert_equal expect, parsed + assert_equal expect.map(&:to_s), parsed.map(&:to_s) end test "parse text with trailing star in the end" do accept = "text/html, application/json, multipart/form-data, text/*" - expect = [Mime[:html], Mime[:json], Mime[:multipart_form], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml]] + expect = [Mime[:html], Mime[:json], Mime[:multipart_form], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml]] parsed = Mime::Type.parse(accept) - assert_equal expect, parsed + assert_equal expect.map(&:to_s), parsed.map(&:to_s) end test "parse text with trailing star" do accept = "text/*" - expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml], Mime[:json]] + expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml], Mime[:json]] parsed = Mime::Type.parse(accept) assert_equal expect.map(&:to_s).sort!, parsed.map(&:to_s).sort! end test "parse application with trailing star" do accept = "application/*" - expect = [Mime[:html], Mime[:js], Mime[:xml], Mime[:rss], Mime[:atom], Mime[:yaml], Mime[:url_encoded_form], Mime[:json], Mime[:pdf], Mime[:zip], Mime[:gzip]] + expect = [Mime[:html], Mime[:js], Mime[:xml], Mime[:rss], Mime[:atom], Mime[:yaml], Mime[:url_encoded_form], Mime[:json], Mime[:pdf], Mime[:zip], Mime[:gzip], Mime[:ogx]] parsed = Mime::Type.parse(accept) assert_equal expect.map(&:to_s).sort!, parsed.map(&:to_s).sort! end -- cgit v1.2.3 From af10a39f88488109cecc6e425e08c083a7e55289 Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Tue, 28 Nov 2017 21:15:01 -0500 Subject: Update send_file headers test to use mp4 as example instead of mpg --- actionpack/test/controller/send_file_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index fd2399e433..7b1a52b277 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -178,7 +178,7 @@ class SendFileTest < ActionController::TestCase "image.jpg" => "image/jpeg", "image.tif" => "image/tiff", "image.gif" => "image/gif", - "movie.mpg" => "video/mpeg", + "movie.mp4" => "video/mp4", "file.zip" => "application/zip", "file.unk" => "application/octet-stream", "zip" => "application/octet-stream" -- cgit v1.2.3 From 9d65ac30fde3977d4773ff6052f31b99a5084f0f Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Wed, 29 Nov 2017 13:08:33 +0900 Subject: Use `credentials` instead of `keyfile` in GCS sevice The `keyfile` was renamed to `credentials` in `google-cloud-storage` 1.8.0. https://github.com/GoogleCloudPlatform/google-cloud-ruby/blob/master/google-cloud-storage/CHANGELOG.md#180--2017-11-14 Although `keyfile` can still be used, but it looks like deprecate. https://github.com/GoogleCloudPlatform/google-cloud-ruby/blob/ddf7b2a856d676316525eb581c1a4cc83ca6097b/google-cloud-storage/lib/google/cloud/storage.rb#L589...L590 Therefore, I think that should use `credentials` in newly generated applications. Ref: https://github.com/GoogleCloudPlatform/google-cloud-ruby/issues/1802 --- Gemfile | 2 +- Gemfile.lock | 36 +++++++++++----------- .../lib/active_storage/service/gcs_service.rb | 2 ++ .../test/service/configurations.example.yml | 2 +- .../rails/app/templates/config/storage.yml.tt | 2 +- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Gemfile b/Gemfile index 3c26d1eb18..32ec87b501 100644 --- a/Gemfile +++ b/Gemfile @@ -92,7 +92,7 @@ end # Active Storage group :storage do gem "aws-sdk-s3", require: false - gem "google-cloud-storage", "~> 1.3", require: false + gem "google-cloud-storage", "~> 1.8", require: false gem "azure-storage", require: false gem "mini_magick" diff --git a/Gemfile.lock b/Gemfile.lock index 70c4097798..1453bddb34 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -118,8 +118,8 @@ GEM activerecord-jdbcsqlite3-adapter (1.3.24) activerecord-jdbc-adapter (~> 1.3.24) jdbc-sqlite3 (>= 3.7.2, < 3.9) - addressable (2.5.1) - public_suffix (~> 2.0, >= 2.0.2) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) amq-protocol (2.2.0) archive-zip (0.7.0) io-like (~> 0.3.0) @@ -209,7 +209,7 @@ GEM daemons (1.2.4) dalli (2.7.6) dante (0.2.0) - declarative (0.0.9) + declarative (0.0.10) declarative-option (0.1.0) delayed_job (4.1.3) activesupport (>= 3.0, < 5.2) @@ -252,25 +252,25 @@ GEM ffi (1.9.18-x86-mingw32) globalid (0.4.1) activesupport (>= 4.2.0) - google-api-client (0.13.1) + google-api-client (0.17.3) addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.5) + googleauth (>= 0.5, < 0.7.0) httpclient (>= 2.8.1, < 3.0) mime-types (~> 3.0) representable (~> 3.0) retriable (>= 2.0, < 4.0) - google-cloud-core (1.0.0) + google-cloud-core (1.1.0) google-cloud-env (~> 1.0) - googleauth (~> 0.5.1) google-cloud-env (1.0.1) faraday (~> 0.11) - google-cloud-storage (1.4.0) + google-cloud-storage (1.9.0) digest-crc (~> 0.4) - google-api-client (~> 0.13.0) - google-cloud-core (~> 1.0) - googleauth (0.5.3) + google-api-client (~> 0.17.0) + google-cloud-core (~> 1.1) + googleauth (~> 0.6.2) + googleauth (0.6.2) faraday (~> 0.12) - jwt (~> 1.4) + jwt (>= 1.4, < 3.0) logging (~> 2.0) memoist (~> 0.12) multi_json (~> 1.11) @@ -289,7 +289,7 @@ GEM jmespath (1.3.1) json (2.1.0) json (2.1.0-java) - jwt (1.5.6) + jwt (2.1.0) kindlerb (1.2.0) mustache nokogiri @@ -329,7 +329,7 @@ GEM msgpack (1.1.0-java) msgpack (1.1.0-x64-mingw32) msgpack (1.1.0-x86-mingw32) - multi_json (1.12.1) + multi_json (1.12.2) multipart-post (2.0.0) mustache (1.0.5) mustermann (1.0.0) @@ -354,7 +354,7 @@ GEM pg (0.19.0-x86-mingw32) powerpack (0.1.1) psych (2.2.4) - public_suffix (2.0.5) + public_suffix (3.0.1) puma (3.9.1) puma (3.9.1-java) que (0.14.0) @@ -429,10 +429,10 @@ GEM rack-protection (>= 1.5.0) redis (>= 3.3.4, < 5) sigdump (0.2.4) - signet (0.7.3) + signet (0.8.1) addressable (~> 2.3) faraday (~> 0.9) - jwt (~> 1.5) + jwt (>= 1.5, < 3.0) multi_json (~> 1.10) sinatra (2.0.0) mustermann (~> 1.0) @@ -517,7 +517,7 @@ DEPENDENCIES dalli (>= 2.2.1) delayed_job delayed_job_active_record - google-cloud-storage (~> 1.3) + google-cloud-storage (~> 1.8) hiredis json (>= 2.0.0) kindlerb (~> 1.2.0) diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb index be6ddf32a0..fd9916634a 100644 --- a/activestorage/lib/active_storage/service/gcs_service.rb +++ b/activestorage/lib/active_storage/service/gcs_service.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +gem "google-cloud-storage", "~> 1.8" + require "google/cloud/storage" require "active_support/core_ext/object/to_query" diff --git a/activestorage/test/service/configurations.example.yml b/activestorage/test/service/configurations.example.yml index 56ed37be5d..43cc013bc8 100644 --- a/activestorage/test/service/configurations.example.yml +++ b/activestorage/test/service/configurations.example.yml @@ -7,7 +7,7 @@ # # gcs: # service: GCS -# keyfile: { +# credentials: { # type: "service_account", # project_id: "", # private_key_id: "", diff --git a/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt index 9bada4b66d..1c0cde0b09 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt @@ -18,7 +18,7 @@ local: # google: # service: GCS # project: your_project -# keyfile: <%%= Rails.root.join("path/to/gcs.keyfile") %> +# credentials: <%%= Rails.root.join("path/to/gcs.keyfile") %> # bucket: your_own_bucket # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) -- cgit v1.2.3 From 5ec53c58f8e900d3ef25de64652c96806c6eb2fe Mon Sep 17 00:00:00 2001 From: Dixit Patel Date: Wed, 29 Nov 2017 11:40:45 +0530 Subject: [ci skip] Correct output for Upcase --- activesupport/lib/active_support/core_ext/string/multibyte.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb index 38224ea5da..075b206d04 100644 --- a/activesupport/lib/active_support/core_ext/string/multibyte.rb +++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb @@ -12,7 +12,7 @@ class String # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string. # # >> "lj".upcase - # => "lj" + # => "LJ" # >> "lj".mb_chars.upcase.to_s # => "LJ" # -- cgit v1.2.3 From f128177eb701373326a3ae91651bf291d0fd245c Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Wed, 29 Nov 2017 01:37:48 -0500 Subject: Register "audio/mp4" mime type with :m4a symbol --- actionpack/lib/action_dispatch/http/mime_types.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index 9f8db397fd..cc41bdda5a 100644 --- a/actionpack/lib/action_dispatch/http/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb @@ -24,7 +24,7 @@ Mime::Type.register "audio/mpeg", :mpg, [], %w(mp1 mp2 mp3) Mime::Type.register "audio/webm", :weba, [], %w(weba) Mime::Type.register "audio/ogg", :ogg, [], %w(oga ogg spx opus) Mime::Type.register "audio/aac", :acc, [], %w(aac) -Mime::Type.register "audio/mp4", :mp4, [], %w(m4a mpg4) +Mime::Type.register "audio/mp4", :m4a, [], %w(m4a mpg4) Mime::Type.register "audio/flac", :flac, [], %w(flac) Mime::Type.register "video/webm", :webm, [], %w(webm) -- cgit v1.2.3 From 0061e0cd28438f4f3f334c5a75eb292e5f110262 Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Wed, 29 Nov 2017 02:29:33 -0500 Subject: Restore mpeg mime type, delete less common mime types See discussion in #31251 --- actionpack/lib/action_dispatch/http/mime_types.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index cc41bdda5a..6d703cb6b1 100644 --- a/actionpack/lib/action_dispatch/http/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb @@ -18,20 +18,15 @@ Mime::Type.register "image/gif", :gif, [], %w(gif) Mime::Type.register "image/bmp", :bmp, [], %w(bmp) Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff) Mime::Type.register "image/svg+xml", :svg -Mime::Type.register "image/webp", :webp, [], %w(webp) -Mime::Type.register "audio/mpeg", :mpg, [], %w(mp1 mp2 mp3) -Mime::Type.register "audio/webm", :weba, [], %w(weba) +Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe) + +Mime::Type.register "audio/mpeg", :mp3, [], %w(mp1 mp2 mp3) Mime::Type.register "audio/ogg", :ogg, [], %w(oga ogg spx opus) -Mime::Type.register "audio/aac", :acc, [], %w(aac) -Mime::Type.register "audio/mp4", :m4a, [], %w(m4a mpg4) -Mime::Type.register "audio/flac", :flac, [], %w(flac) +Mime::Type.register "audio/aac", :m4a, %( audio/mp4 ), %w(m4a mpg4 aac) Mime::Type.register "video/webm", :webm, [], %w(webm) Mime::Type.register "video/mp4", :mp4, [], %w(mp4 m4v) -Mime::Type.register "video/ogg", :ogv, [], %w(ogv) - -Mime::Type.register "application/ogx", :ogx, [], %w(ogx) Mime::Type.register "font/otf", :otf, [], %w(otf) Mime::Type.register "font/ttf", :ttf, [], %w(ttf) -- cgit v1.2.3 From d41d586e4e00990a8b5e1e62b75857fea0effaf4 Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Wed, 29 Nov 2017 02:37:39 -0500 Subject: Fix typo in mime type registering --- actionpack/lib/action_dispatch/http/mime_types.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index 6d703cb6b1..342e6de312 100644 --- a/actionpack/lib/action_dispatch/http/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb @@ -23,7 +23,7 @@ Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe) Mime::Type.register "audio/mpeg", :mp3, [], %w(mp1 mp2 mp3) Mime::Type.register "audio/ogg", :ogg, [], %w(oga ogg spx opus) -Mime::Type.register "audio/aac", :m4a, %( audio/mp4 ), %w(m4a mpg4 aac) +Mime::Type.register "audio/aac", :m4a, %w( audio/mp4 ), %w(m4a mpg4 aac) Mime::Type.register "video/webm", :webm, [], %w(webm) Mime::Type.register "video/mp4", :mp4, [], %w(mp4 m4v) -- cgit v1.2.3 From e3d658e319bd0ab179fc0e12329ed4dc18bce584 Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Wed, 29 Nov 2017 02:51:31 -0500 Subject: Fix tests: Remove ogx mime type from tests --- actionpack/test/dispatch/mime_type_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index c0b4034d28..6854783386 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -51,7 +51,7 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse application with trailing star" do accept = "application/*" - expect = [Mime[:html], Mime[:js], Mime[:xml], Mime[:rss], Mime[:atom], Mime[:yaml], Mime[:url_encoded_form], Mime[:json], Mime[:pdf], Mime[:zip], Mime[:gzip], Mime[:ogx]] + expect = [Mime[:html], Mime[:js], Mime[:xml], Mime[:rss], Mime[:atom], Mime[:yaml], Mime[:url_encoded_form], Mime[:json], Mime[:pdf], Mime[:zip], Mime[:gzip]] parsed = Mime::Type.parse(accept) assert_equal expect.map(&:to_s).sort!, parsed.map(&:to_s).sort! end -- cgit v1.2.3 From f4c08d14995ba9d5caf0f209d3a95308f93bec6a Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 29 Nov 2017 17:18:39 +0900 Subject: Mention about Ruby 2.4 Unicode case mappings in `mb_chars` example [ci skip] (#31275) https://www.ruby-lang.org/en/news/2016/09/08/ruby-2-4-0-preview2-released/ --- activesupport/lib/active_support/core_ext/string/multibyte.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb index 075b206d04..07c0d16398 100644 --- a/activesupport/lib/active_support/core_ext/string/multibyte.rb +++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb @@ -12,10 +12,12 @@ class String # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string. # # >> "lj".upcase - # => "LJ" + # => "lj" # >> "lj".mb_chars.upcase.to_s # => "LJ" # + # NOTE: An above example is useful for pre Ruby 2.4. Ruby 2.4 supports Unicode case mappings. + # # == Method chaining # # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows -- cgit v1.2.3 From 9d6e288ee96d6241f864dbf90211c37b14a57632 Mon Sep 17 00:00:00 2001 From: eileencodes Date: Wed, 29 Nov 2017 10:54:56 -0500 Subject: Make screenshots default to "simple" format Not everyone uses iTerm2 and whereas Terminal.app on a mac just ignores that and outputs the path, other terminals like those on Ubuntu do not. A friendlier default is one that works by default. Closes #31159 Closes #30957 --- .../system_testing/test_helpers/screenshot_helper.rb | 16 ++++++---------- .../dispatch/system_testing/screenshot_helper_test.rb | 7 +++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb index 6c337cdc31..df0c5d3f0e 100644 --- a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb @@ -15,12 +15,11 @@ module ActionDispatch # # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to # control the output. Possible values are: - # * [+inline+ (default)] display the screenshot in the terminal using the + # * [+simple+ (default)] Only displays the screenshot path. + # This is the default value. + # * [+inline+] Display the screenshot in the terminal using the # iTerm image protocol (https://iterm2.com/documentation-images.html). - # * [+simple+] only display the screenshot path. - # This is the default value if the +CI+ environment variables - # is defined. - # * [+artifact+] display the screenshot in the terminal, using the terminal + # * [+artifact+] Display the screenshot in the terminal, using the terminal # artifact format (https://buildkite.github.io/terminal/inline-images/). def take_screenshot save_image @@ -59,11 +58,8 @@ module ActionDispatch # Environment variables have priority output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] || ENV["CAPYBARA_INLINE_SCREENSHOT"] - # If running in a CI environment, default to simple - output_type ||= "simple" if ENV["CI"] - - # Default - output_type ||= "inline" + # Default to outputting a path to the screenshot + output_type ||= "simple" output_type end diff --git a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb index 2afda31cf5..264844fc7d 100644 --- a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb +++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb @@ -35,6 +35,11 @@ class ScreenshotHelperTest < ActiveSupport::TestCase end end + test "defaults to simple output for the screenshot" do + new_test = DrivenBySeleniumWithChrome.new("x") + assert_equal "simple", new_test.send(:output_type) + end + test "display_image return artifact format when specify RAILS_SYSTEM_TESTING_SCREENSHOT environment" do begin original_output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] @@ -42,6 +47,8 @@ class ScreenshotHelperTest < ActiveSupport::TestCase new_test = DrivenBySeleniumWithChrome.new("x") + assert_equal "artifact", new_test.send(:output_type) + Rails.stub :root, Pathname.getwd do new_test.stub :passed?, false do assert_match %r|url=artifact://.+?tmp/screenshots/failures_x\.png|, new_test.send(:display_image) -- cgit v1.2.3 From 1bee2fb600c07625b830afd33b43ead3364c9715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 29 Nov 2017 14:56:27 -0500 Subject: Build the root folder before specific files Fixes #31282. --- railties/lib/rails/generators/rails/app/app_generator.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 874bd772c7..bf4570db90 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -348,6 +348,14 @@ module Rails build(:public_directory) end + def create_tmp_files + build(:tmp) + end + + def create_vendor_files + build(:vendor) + end + def create_test_files build(:test) unless options[:skip_test] end @@ -360,14 +368,6 @@ module Rails build(:storage) unless skip_active_storage? end - def create_tmp_files - build(:tmp) - end - - def create_vendor_files - build(:vendor) - end - def delete_app_assets_if_api_option if options[:api] remove_dir "app/assets" -- cgit v1.2.3 From d2aca50bbd69c7a12cdcbeaba3dbef2679927b90 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 30 Nov 2017 08:58:32 +0900 Subject: Add :nodoc: to `StatementPool` which is internal used [ci skip] In #30510, `StatementPool` in `AbstractMysqlAdapter` was hidden in the doc. But that class is also had in sqlite3 and postgresql adapters and the base class is :nodoc: class. --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 3 +-- activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 7b27f6b7a0..27011bfe92 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -166,7 +166,7 @@ module ActiveRecord { concurrently: "CONCURRENTLY" } end - class StatementPool < ConnectionAdapters::StatementPool + class StatementPool < ConnectionAdapters::StatementPool # :nodoc: def initialize(connection, max) super(max) @connection = connection @@ -182,7 +182,6 @@ module ActiveRecord end private - def dealloc(key) @connection.query "DEALLOCATE #{key}" if connection_active? rescue PG::Error diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 670afa3684..daece2bffd 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -90,9 +90,8 @@ module ActiveRecord # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true class_attribute :represent_boolean_as_integer, default: false - class StatementPool < ConnectionAdapters::StatementPool + class StatementPool < ConnectionAdapters::StatementPool # :nodoc: private - def dealloc(stmt) stmt[:stmt].close unless stmt[:stmt].closed? end -- cgit v1.2.3 From f7e3c686685fb89e67293440d24356f93fa34847 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 30 Nov 2017 08:01:21 +0900 Subject: Do not overwrite by default if credentials already exists Fixes #31286 --- .../rails/generators/rails/credentials/credentials_generator.rb | 6 ++++-- railties/test/commands/credentials_test.rb | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb index 067479c672..01a5b502f9 100644 --- a/railties/lib/rails/generators/rails/credentials/credentials_generator.rb +++ b/railties/lib/rails/generators/rails/credentials/credentials_generator.rb @@ -8,7 +8,7 @@ module Rails module Generators class CredentialsGenerator < Base def add_credentials_file - unless credentials.exist? + unless credentials.content_path.exist? template = credentials_template say "Adding #{credentials.content_path} to store encrypted credentials." @@ -26,7 +26,9 @@ module Rails end def add_credentials_file_silently(template = nil) - credentials.write(credentials_template) + unless credentials.content_path.exist? + credentials.write(credentials_template) + end end private diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb index 4ef827fcf1..f1bb1ef08a 100644 --- a/railties/test/commands/credentials_test.rb +++ b/railties/test/commands/credentials_test.rb @@ -39,6 +39,14 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase end end + test "edit command does not overwrite by default if credentials already exists" do + run_edit_command(editor: "eval echo api_key: abc >") + assert_match(/api_key: abc/, run_show_command) + + run_edit_command + assert_match(/api_key: abc/, run_show_command) + end + private def run_edit_command(editor: "cat") switch_env("EDITOR", editor) do -- cgit v1.2.3 From 5b2e826e6d5a9031133669be03e3b04861c17e4b Mon Sep 17 00:00:00 2001 From: Noah Davis Date: Wed, 29 Nov 2017 23:16:04 -0500 Subject: Keep current Code Climate behavior before upgrade If you merge these changes now (*before Monday, December 4th*), Code Climate will run the same analysis on Rails as it always has on your pull requests. *If you do not merge these changes*, when Code Climate migrates Rails to our new analysis, Code Climate will continue to run Rubocop, but it will ALSO run Code Climate's new maintainability checks (https://codeclimate.com/blog/10-point-technical-debt-assessment). You may want this ... you may not. Just want to make sure you knew the quick option to disable them if it's a problem! --- .codeclimate.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.codeclimate.yml b/.codeclimate.yml index 1f0f067c5b..d59a0780d1 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,3 +1,25 @@ +checks: + argument-count: + enabled: false + complex-logic: + enabled: false + file-lines: + enabled: false + method-complexity: + enabled: false + method-count: + enabled: false + method-lines: + enabled: false + nested-control-flow: + enabled: false + return-statements: + enabled: false + similar-code: + enabled: false + identical-code: + enabled: false + engines: rubocop: enabled: true -- cgit v1.2.3 From b6baf0c88411824ce99f1ad4b9de64fa37ad96ea Mon Sep 17 00:00:00 2001 From: Yauheni Dakuka Date: Thu, 30 Nov 2017 09:25:50 +0300 Subject: Cosmetic changes [ci skip] --- guides/source/3_2_release_notes.md | 4 ++-- guides/source/4_1_release_notes.md | 2 +- guides/source/4_2_release_notes.md | 2 +- guides/source/action_view_overview.md | 4 ++-- guides/source/asset_pipeline.md | 8 +++---- guides/source/caching_with_rails.md | 2 +- guides/source/i18n.md | 2 +- guides/source/initialization.md | 2 +- guides/source/testing.md | 2 +- guides/source/upgrading_ruby_on_rails.md | 26 +++++++++++------------ guides/source/working_with_javascript_in_rails.md | 2 +- 11 files changed, 28 insertions(+), 28 deletions(-) diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index f6571544f9..ae6eb27f35 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -36,7 +36,7 @@ TIP: Note that Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails. * `coffee-rails ~> 3.2.1` * `uglifier >= 1.0.3` -* Rails 3.2 deprecates `vendor/plugins` and Rails 4.0 will remove them completely. You can start replacing these plugins by extracting them as gems and adding them in your Gemfile. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`. +* Rails 3.2 deprecates `vendor/plugins` and Rails 4.0 will remove them completely. You can start replacing these plugins by extracting them as gems and adding them in your `Gemfile`. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`. * There are a couple of new configuration changes you'd want to add in `config/environments/development.rb`: @@ -156,7 +156,7 @@ Railties will create indexes for `title` and `author` with the latter being a unique index. Some types such as decimal accept custom options. In the example, `price` will be a decimal column with precision and scale set to 7 and 2 respectively. -* Turn gem has been removed from default Gemfile. +* Turn gem has been removed from default `Gemfile`. * Remove old plugin generator `rails generate plugin` in favor of `rails plugin new` command. diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md index 6bf65757ec..2c5e665e33 100644 --- a/guides/source/4_1_release_notes.md +++ b/guides/source/4_1_release_notes.md @@ -274,7 +274,7 @@ for detailed changes. * The [Spring application preloader](https://github.com/rails/spring) is now installed by default for new applications. It uses the development group of - the Gemfile, so will not be installed in + the `Gemfile`, so will not be installed in production. ([Pull Request](https://github.com/rails/rails/pull/12958)) * `BACKTRACE` environment variable to show unfiltered backtraces for test diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index 036a310ac8..7105df5634 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -368,7 +368,7 @@ Please refer to the [Changelog][railties] for detailed changes. ### Notable changes -* Introduced `web-console` in the default application Gemfile. +* Introduced `web-console` in the default application `Gemfile`. ([Pull Request](https://github.com/rails/rails/pull/11667)) * Added a `required` option to the model generator for associations. diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index c1e02745de..fde2040173 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -149,10 +149,10 @@ end #### Jbuilder [Jbuilder](https://github.com/rails/jbuilder) is a gem that's -maintained by the Rails team and included in the default Rails Gemfile. +maintained by the Rails team and included in the default Rails `Gemfile`. It's similar to Builder, but is used to generate JSON, instead of XML. -If you don't have it, you can add the following to your Gemfile: +If you don't have it, you can add the following to your `Gemfile`: ```ruby gem 'jbuilder' diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 805b0f0d62..e6d5aed135 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -35,7 +35,7 @@ rails new appname --skip-sprockets ``` Rails automatically adds the `sass-rails`, `coffee-rails` and `uglifier` -gems to your Gemfile, which are used by Sprockets for asset compression: +gems to your `Gemfile`, which are used by Sprockets for asset compression: ```ruby gem 'sass-rails' @@ -44,8 +44,8 @@ gem 'coffee-rails' ``` Using the `--skip-sprockets` option will prevent Rails from adding -them to your Gemfile, so if you later want to enable -the asset pipeline you will have to add those gems to your Gemfile. Also, +them to your `Gemfile`, so if you later want to enable +the asset pipeline you will have to add those gems to your `Gemfile`. Also, creating an application with the `--skip-sprockets` option will generate a slightly different `config/application.rb` file, with a require statement for the sprockets railtie that is commented-out. You will have to remove @@ -850,7 +850,7 @@ This mode uses more memory, performs more poorly than the default and is not recommended. If you are deploying a production application to a system without any -pre-existing JavaScript runtimes, you may want to add one to your Gemfile: +pre-existing JavaScript runtimes, you may want to add one to your `Gemfile`: ```ruby group :production do diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 31bc478015..780e69c146 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -32,7 +32,7 @@ Basic Caching This is an introduction to three types of caching techniques: page, action and fragment caching. By default Rails provides fragment caching. In order to use page and action caching you will need to add `actionpack-page_caching` and -`actionpack-action_caching` to your Gemfile. +`actionpack-action_caching` to your `Gemfile`. By default, caching is only enabled in your production environment. To play around with caching locally you'll want to enable caching in your local diff --git a/guides/source/i18n.md b/guides/source/i18n.md index e6aa6181cc..2b545e6b82 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -977,7 +977,7 @@ en: ``` NOTE: In order to use this helper, you need to install [DynamicForm](https://github.com/joelmoss/dynamic_form) -gem by adding this line to your Gemfile: `gem 'dynamic_form'`. +gem by adding this line to your `Gemfile`: `gem 'dynamic_form'`. ### Translations for Action Mailer E-Mail Subjects diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 1541ea38cd..c4f1df487b 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -93,7 +93,7 @@ require 'bundler/setup' # Set up gems listed in the Gemfile. In a standard Rails application, there's a `Gemfile` which declares all dependencies of the application. `config/boot.rb` sets -`ENV['BUNDLE_GEMFILE']` to the location of this file. If the Gemfile +`ENV['BUNDLE_GEMFILE']` to the location of this file. If the `Gemfile` exists, then `bundler/setup` is required. The require is used by Bundler to configure the load path for your Gemfile's dependencies. diff --git a/guides/source/testing.md b/guides/source/testing.md index 8416fd163d..e0a2d281d9 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -645,7 +645,7 @@ system tests should live. If you want to change the default settings you can change what the system tests are "driven by". Say you want to change the driver from Selenium to -Poltergeist. First add the `poltergeist` gem to your Gemfile. Then in your +Poltergeist. First add the `poltergeist` gem to your `Gemfile`. Then in your `application_system_test_case.rb` file do the following: ```ruby diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 9bc87e4bf0..eae73c3e1b 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -45,7 +45,7 @@ TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterp ### The Update Task Rails provides the `app:update` task (`rake rails:update` on 4.2 and earlier). After updating the Rails version -in the Gemfile, run this task. +in the `Gemfile`, run this task. This will help you with the creation of new files and changes of old files in an interactive session. @@ -179,7 +179,7 @@ See [#19034](https://github.com/rails/rails/pull/19034) for more details. `assigns` and `assert_template` have been extracted to the `rails-controller-testing` gem. To continue using these methods in your controller tests, add `gem 'rails-controller-testing'` to -your Gemfile. +your `Gemfile`. If you are using Rspec for testing, please see the extra configuration required in the gem's documentation. @@ -212,7 +212,7 @@ true. `ActiveModel::Serializers::Xml` has been extracted from Rails to the `activemodel-serializers-xml` gem. To continue using XML serialization in your application, add `gem 'activemodel-serializers-xml'` -to your Gemfile. +to your `Gemfile`. ### Removed Support for Legacy `mysql` Database Adapter @@ -278,7 +278,7 @@ You can now just call the dependency once with a wildcard. ### `ActionView::Helpers::RecordTagHelper` moved to external gem (record_tag_helper) -`content_tag_for` and `div_for` have been removed in favor of just using `content_tag`. To continue using the older methods, add the `record_tag_helper` gem to your Gemfile: +`content_tag_for` and `div_for` have been removed in favor of just using `content_tag`. To continue using the older methods, add the `record_tag_helper` gem to your `Gemfile`: ```ruby gem 'record_tag_helper', '~> 1.0' @@ -415,7 +415,7 @@ First, add `gem 'web-console', '~> 2.0'` to the `:development` group in your `Ge ### Responders -`respond_with` and the class-level `respond_to` methods have been extracted to the `responders` gem. To use them, simply add `gem 'responders', '~> 2.0'` to your Gemfile. Calls to `respond_with` and `respond_to` (again, at the class level) will no longer work without having included the `responders` gem in your dependencies: +`respond_with` and the class-level `respond_to` methods have been extracted to the `responders` gem. To use them, simply add `gem 'responders', '~> 2.0'` to your `Gemfile`. Calls to `respond_with` and `respond_to` (again, at the class level) will no longer work without having included the `responders` gem in your dependencies: ```ruby # app/controllers/users_controller.rb @@ -559,7 +559,7 @@ Read the [gem's readme](https://github.com/rails/rails-html-sanitizer) for more The documentation for `PermitScrubber` and `TargetScrubber` explains how you can gain complete control over when and how elements should be stripped. -If your application needs to use the old sanitizer implementation, include `rails-deprecated_sanitizer` in your Gemfile: +If your application needs to use the old sanitizer implementation, include `rails-deprecated_sanitizer` in your `Gemfile`: ```ruby gem 'rails-deprecated_sanitizer' @@ -617,7 +617,7 @@ migration DSL counterpart. The migration procedure is as follows: -1. remove `gem "foreigner"` from the Gemfile. +1. remove `gem "foreigner"` from the `Gemfile`. 2. run `bundle install`. 3. run `bin/rake db:schema:dump`. 4. make sure that `db/schema.rb` contains every foreign key definition with @@ -769,7 +769,7 @@ and has been removed from Rails. If your application currently depends on MultiJSON directly, you have a few options: -1. Add 'multi_json' to your Gemfile. Note that this might cease to work in the future +1. Add 'multi_json' to your `Gemfile`. Note that this might cease to work in the future 2. Migrate away from MultiJSON by using `obj.to_json`, and `JSON.parse(str)` instead. @@ -810,7 +810,7 @@ part of the rewrite, the following features have been removed from the encoder: If your application depends on one of these features, you can get them back by adding the [`activesupport-json_encoder`](https://github.com/rails/activesupport-json_encoder) -gem to your Gemfile. +gem to your `Gemfile`. #### JSON representation of Time objects @@ -1135,7 +1135,7 @@ full support for the last few changes in the specification. ### Gemfile -Rails 4.0 removed the `assets` group from Gemfile. You'd need to remove that +Rails 4.0 removed the `assets` group from `Gemfile`. You'd need to remove that line from your `Gemfile` when upgrading. You should also update your application file (in `config/application.rb`): @@ -1147,7 +1147,7 @@ Bundler.require(*Rails.groups) ### vendor/plugins -Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must replace any plugins by extracting them to gems and adding them to your Gemfile. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`. +Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must replace any plugins by extracting them to gems and adding them to your `Gemfile`. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`. ### Active Record @@ -1214,7 +1214,7 @@ end ### Active Resource -Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your Gemfile. +Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your `Gemfile`. ### Active Model @@ -1414,7 +1414,7 @@ config.active_record.mass_assignment_sanitizer = :strict ### vendor/plugins -Rails 3.2 deprecates `vendor/plugins` and Rails 4.0 will remove them completely. While it's not strictly necessary as part of a Rails 3.2 upgrade, you can start replacing any plugins by extracting them to gems and adding them to your Gemfile. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`. +Rails 3.2 deprecates `vendor/plugins` and Rails 4.0 will remove them completely. While it's not strictly necessary as part of a Rails 3.2 upgrade, you can start replacing any plugins by extracting them to gems and adding them to your `Gemfile`. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`. ### Active Record diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 86746a5ae0..c3dff1772c 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -492,7 +492,7 @@ replace the entire `` of the page with the `` of the response. It will then use PushState to change the URL to the correct one, preserving refresh semantics and giving you pretty URLs. -The only thing you have to do to enable Turbolinks is have it in your Gemfile, +The only thing you have to do to enable Turbolinks is have it in your `Gemfile`, and put `//= require turbolinks` in your JavaScript manifest, which is usually `app/assets/javascripts/application.js`. -- cgit v1.2.3 From 1dca75c2c8f930b58d86cd2216af5e14307b3e53 Mon Sep 17 00:00:00 2001 From: Greg Navis Date: Sat, 13 May 2017 03:09:58 +0200 Subject: Add support for PostgreSQL operator classes to add_index Add support for specifying non-default operator classes in PostgreSQL indexes. An example CREATE INDEX query that becomes possible is: CREATE INDEX users_name ON users USING gist (name gist_trgm_ops); Previously it was possible to specify the `gist` index but not the custom operator class. The `add_index` call for the above query is: add_index :users, :name, using: :gist, opclasses: {name: :gist_trgm_ops} --- activerecord/CHANGELOG.md | 8 ++++++ .../abstract/schema_definitions.rb | 4 ++- .../abstract/schema_statements.rb | 28 +++++++++++++++++-- .../postgresql/schema_statements.rb | 32 ++++++++++++++-------- .../connection_adapters/postgresql_adapter.rb | 17 ++++++++++++ activerecord/lib/active_record/schema_dumper.rb | 1 + .../adapters/postgresql/active_schema_test.rb | 3 ++ .../adapters/postgresql/postgresql_adapter_test.rb | 10 +++---- .../test/cases/adapters/postgresql/schema_test.rb | 32 ++++++++++++++++++++++ 9 files changed, 115 insertions(+), 20 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 60ceffac5e..048a1eeb2f 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,11 @@ +* Add support for PostgreSQL operator classes to `add_index`. + + Example: + + add_index :users, :name, using: :gist, opclass: name: :gist_trgm_ops + + *Greg Navis* + * Don't allow scopes to be defined which conflict with instance methods on `Relation`. Fixes #31120. 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 be2f625d74..b66009bdc6 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -6,7 +6,7 @@ module ActiveRecord # this type are typically created and returned by methods in database # adapters. e.g. ActiveRecord::ConnectionAdapters::MySQL::SchemaStatements#indexes class IndexDefinition # :nodoc: - attr_reader :table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment + attr_reader :table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :opclass, :comment def initialize( table, name, @@ -17,6 +17,7 @@ module ActiveRecord where: nil, type: nil, using: nil, + opclass: {}, comment: nil ) @table = table @@ -28,6 +29,7 @@ module ActiveRecord @where = where @type = type @using = using + @opclass = opclass @comment = comment end end 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 9b7345f7c3..2a335e8f2b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -738,6 +738,29 @@ module ActiveRecord # # Note: only supported by PostgreSQL and MySQL # + # ====== Creating an index with a specific operator class + # + # add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops) + # + # generates: + # + # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL + # + # add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops }) + # + # generates: + # + # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL + # + # add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops }) + # + # generates: + # + # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL + # + # + # Note: only supported by PostgreSQL + # # ====== Creating an index with a specific type # # add_index(:developers, :name, type: :fulltext) @@ -1120,7 +1143,7 @@ module ActiveRecord def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc: column_names = index_column_names(column_name) - options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type) + options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclass) index_type = options[:type].to_s if options.key?(:type) index_type ||= options[:unique] ? "UNIQUE" : "" @@ -1186,7 +1209,8 @@ module ActiveRecord quoted_columns end - # Overridden by the MySQL adapter for supporting index lengths + # Overridden by the MySQL adapter for supporting index lengths and by + # the PostgreSQL adapter for supporting operator classes. def add_options_for_index_columns(quoted_columns, **options) if supports_index_sort_order? quoted_columns = add_index_sort_order(quoted_columns, options) 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 846e721983..15a1dfd102 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -87,10 +87,7 @@ module ActiveRecord result = query(<<-SQL, "SCHEMA") SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid, - pg_catalog.obj_description(i.oid, 'pg_class') AS comment, - (SELECT COUNT(*) FROM pg_opclass o - JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c - ON o.oid = c.oid WHERE o.opcdefault = 'f') + pg_catalog.obj_description(i.oid, 'pg_class') AS comment FROM pg_class t INNER JOIN pg_index d ON t.oid = d.indrelid INNER JOIN pg_class i ON d.indexrelid = i.oid @@ -109,11 +106,10 @@ module ActiveRecord inddef = row[3] oid = row[4] comment = row[5] - opclass = row[6] using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten - if indkey.include?(0) || opclass > 0 + if indkey.include?(0) columns = expressions else columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact @@ -123,10 +119,21 @@ module ActiveRecord AND a.attnum IN (#{indkey.join(",")}) SQL - # add info on sort order for columns (only desc order is explicitly specified, asc is the default) - orders = Hash[ - expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] } - ] + orders = {} + opclasses = {} + + # add info on sort order (only desc order is explicitly specified, asc is the default) + # and non-default opclasses + expressions.scan(/(\w+)(?: (?!DESC)(\w+))?(?: (DESC))?/).each do |column, opclass, desc| + opclasses[column] = opclass if opclass + orders[column] = :desc if desc + end + + # Use a string for the opclass description (instead of a hash) when all columns + # have the same opclass specified. + if columns.count == opclasses.count && opclasses.values.uniq.count == 1 + opclasses = opclasses.values.first + end end IndexDefinition.new( @@ -137,6 +144,7 @@ module ActiveRecord orders: orders, where: where, using: using.to_sym, + opclass: opclasses, comment: comment.presence ) end @@ -458,8 +466,8 @@ module ActiveRecord end def add_index(table_name, column_name, options = {}) #:nodoc: - index_name, index_type, index_columns, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options) - execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}").tap do + index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options) + execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns_and_opclasses})#{index_options}").tap do execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 7b27f6b7a0..eee61780b7 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -388,6 +388,23 @@ module ActiveRecord private + def add_index_opclass(column_names, options = {}) + opclass = + if options[:opclass].is_a?(Hash) + options[:opclass].symbolize_keys + else + Hash.new { |hash, column| hash[column] = options[:opclass].to_s } + end + column_names.each do |name, column| + column << " #{opclass[name]}" if opclass[name].present? + end + end + + def add_options_for_index_columns(quoted_columns, **options) + quoted_columns = add_index_opclass(quoted_columns, options) + super + end + # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html VALUE_LIMIT_VIOLATION = "22001" NUMERIC_VALUE_OUT_OF_RANGE = "22003" diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 66f7d29886..8cb2851557 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -189,6 +189,7 @@ HEADER index_parts << "where: #{index.where.inspect}" if index.where index_parts << "using: #{index.using.inspect}" if !@connection.default_index_type?(index) index_parts << "type: #{index.type.inspect}" if index.type + index_parts << "opclass: #{index.opclass.inspect}" if index.opclass.present? index_parts << "comment: #{index.comment.inspect}" if index.comment index_parts end diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb index 9929237546..99c53dadeb 100644 --- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb @@ -59,6 +59,9 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase assert_equal expected, add_index(:people, "lower(last_name)", using: type, unique: true) end + expected = %(CREATE INDEX "index_people_on_last_name" ON "people" USING gist ("last_name" bpchar_pattern_ops)) + assert_equal expected, add_index(:people, :last_name, using: :gist, opclass: { last_name: :bpchar_pattern_ops }) + assert_raise ArgumentError do add_index(:people, :last_name, algorithm: :copy) end diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index f199519d86..1951230c8a 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -248,12 +248,12 @@ module ActiveRecord def test_index_with_opclass with_example_table do - @connection.add_index "ex", "data varchar_pattern_ops" - index = @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data_varchar_pattern_ops" } - assert_equal "data varchar_pattern_ops", index.columns + @connection.add_index "ex", "data", opclass: "varchar_pattern_ops" + index = @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data" } + assert_equal ["data"], index.columns - @connection.remove_index "ex", "data varchar_pattern_ops" - assert_not @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data_varchar_pattern_ops" } + @connection.remove_index "ex", "data" + assert_not @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data" } end end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 5a64da028b..a6ae18a826 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -500,6 +500,38 @@ class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase end end +class SchemaIndexOpclassTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table "trains" do |t| + t.string :name + t.text :description + end + end + + teardown do + @connection.drop_table "trains", if_exists: true + end + + def test_string_opclass_is_dumped + @connection.execute "CREATE INDEX trains_name_and_description ON trains USING btree(name text_pattern_ops, description text_pattern_ops)" + + output = dump_table_schema "trains" + + assert_match(/opclass: "text_pattern_ops"/, output) + end + + def test_non_default_opclass_is_dumped + @connection.execute "CREATE INDEX trains_name_and_description ON trains USING btree(name, description text_pattern_ops)" + + output = dump_table_schema "trains" + + assert_match(/opclass: \{"description"=>"text_pattern_ops"\}/, output) + end +end + class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCase setup do @connection = ActiveRecord::Base.connection -- cgit v1.2.3 From 96d17ecba8e374cd1ce4db0cee8c0dca057e3ff7 Mon Sep 17 00:00:00 2001 From: Dixit Patel Date: Thu, 30 Nov 2017 17:05:48 +0530 Subject: [ci skip] Updated links for uncorn which redirect & added https for nginx link --- guides/source/configuring.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 4bfcc1e21a..fee644d4d4 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -1005,11 +1005,11 @@ Deploying your application using a reverse proxy has definite advantages over tr Many modern web servers can be used as a proxy server to balance third-party elements such as caching servers or application servers. -One such application server you can use is [Unicorn](http://unicorn.bogomips.org/) to run behind a reverse proxy. +One such application server you can use is [Unicorn](https://bogomips.org/unicorn/) to run behind a reverse proxy. In this case, you would need to configure the proxy server (NGINX, Apache, etc) to accept connections from your application server (Unicorn). By default Unicorn will listen for TCP connections on port 8080, but you can change the port or configure it to use sockets instead. -You can find more information in the [Unicorn readme](http://unicorn.bogomips.org/README.html) and understand the [philosophy](http://unicorn.bogomips.org/PHILOSOPHY.html) behind it. +You can find more information in the [Unicorn readme](https://bogomips.org/unicorn/README.html) and understand the [philosophy](https://bogomips.org/unicorn/PHILOSOPHY.html) behind it. Once you've configured the application server, you must proxy requests to it by configuring your web server appropriately. For example your NGINX config may include: @@ -1037,7 +1037,7 @@ server { } ``` -Be sure to read the [NGINX documentation](http://nginx.org/en/docs/) for the most up-to-date information. +Be sure to read the [NGINX documentation](https://nginx.org/en/docs/) for the most up-to-date information. Rails Environment Settings -- cgit v1.2.3 From eb9ff5fd39ca3af20cb95b723ee622ceef10310d Mon Sep 17 00:00:00 2001 From: Anton Rieder Date: Thu, 30 Nov 2017 16:34:06 +0100 Subject: Move system test dependencies to test group --- .../lib/rails/generators/rails/app/templates/Gemfile.tt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt index 61026f5182..e3ed3e7c11 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt @@ -41,13 +41,6 @@ gem 'bootsnap', '>= 1.1.0', require: false group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] - <%- if depends_on_system_test? -%> - # Adds support for Capybara system testing and selenium driver - gem 'capybara', '~> 2.15' - gem 'selenium-webdriver' - # Easy installation and use of chromedriver to run system tests with Chrome - gem 'chromedriver-helper' - <%- end -%> end group :development do @@ -70,6 +63,16 @@ group :development do <% end -%> <% end -%> end + +<%- if depends_on_system_test? -%> +group :test do + # Adds support for Capybara system testing and selenium driver + gem 'capybara', '~> 2.15' + gem 'selenium-webdriver' + # Easy installation and use of chromedriver to run system tests with Chrome + gem 'chromedriver-helper' +end +<%- end -%> <% end -%> # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -- cgit v1.2.3 From 0185aae747676e636a52eb079a0a10a6f053fa2c Mon Sep 17 00:00:00 2001 From: eileencodes Date: Thu, 30 Nov 2017 12:26:33 -0500 Subject: Add changelog entry for 9d6e28 Since this changes a default setting a changelog entry is important. --- actionpack/CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index c8fb34ed52..d120d15770 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,15 @@ +* Changed the default system test screenshot output from `inline` to `simple`. + + `inline` works well for iTerm2 but not everyone uses iTerm2. Some terminals like + Terminal.app ignore the `inline` and output the path to the file since it can't + render the image. Other terminals, like those on Ubuntu, cannot handle the image + inline, but also don't handle it gracefully and instead of outputting the file + path, it dumps binary into the terminal. + + Commit 9d6e28 fixes this by changing the default for screenshot to be `simple`. + + *Eileen M. Uchitelle* + * Register most popular audio/video/font mime types supported by modern browsers. *Guillermo Iguaran* -- cgit v1.2.3 From 0dc3b6535dfb8127f568bb4abc2fb09118ce4b96 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 1 Dec 2017 11:49:36 +0900 Subject: Maintain raising `RecordNotFound` for class level `update` and` destroy` In 35836019, class level `update` and `destroy` suppressed `RecordNotFound` to ensure returning affected objects. But `RecordNotFound` is a common exception caught by a `rescue_from` handler. So changing the behavior when a typical `params[:id]` is passed has a compatibility problem. The previous behavior should not be changed. Fixes #31301. --- activerecord/lib/active_record/persistence.rb | 13 +++++++++---- activerecord/test/cases/persistence_test.rb | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 4e1b05dbf6..17459f2505 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -99,7 +99,11 @@ module ActiveRecord # for updating all records in a single query. def update(id = :all, attributes) if id.is_a?(Array) - id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }.compact + id.map.with_index { |one_id, idx| + object = find_by(primary_key => one_id) + object.update(attributes[idx]) if object + object + }.compact elsif id == :all all.each { |record| record.update(attributes) } else @@ -112,7 +116,6 @@ module ActiveRecord object.update(attributes) object end - rescue RecordNotFound end # Destroy an object (or multiple objects) that has the given id. The object is instantiated first, @@ -136,11 +139,13 @@ module ActiveRecord # Todo.destroy(todos) def destroy(id) if id.is_a?(Array) - id.map { |one_id| destroy(one_id) }.compact + id.map { |one_id| + object = find_by(primary_key => one_id) + object.destroy if object + }.compact else find(id).destroy end - rescue RecordNotFound end # Deletes the row with a primary key matching the +id+ argument, using a diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index f088c064f5..643032e7cf 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -473,10 +473,18 @@ class PersistenceTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } end - def test_record_not_found_exception + def test_find_raises_record_not_found_exception assert_raise(ActiveRecord::RecordNotFound) { Topic.find(99999) } end + def test_update_raises_record_not_found_exception + assert_raise(ActiveRecord::RecordNotFound) { Topic.update(99999, approved: true) } + end + + def test_destroy_raises_record_not_found_exception + assert_raise(ActiveRecord::RecordNotFound) { Topic.destroy(99999) } + end + def test_update_all assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'") assert_equal "bulk updated!", Topic.find(1).content @@ -938,7 +946,9 @@ class PersistenceTest < ActiveRecord::TestCase should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") Topic.find(1).replies << should_not_be_destroyed_reply - assert_nil Topic.where("1=0").scoping { Topic.destroy(1) } + assert_raise(ActiveRecord::RecordNotFound) do + Topic.where("1=0").scoping { Topic.destroy(1) } + end assert_nothing_raised { Topic.find(1) } assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) } -- cgit v1.2.3 From d041a1dcbaced9f44ed976a48312c9f492fffe5e Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Thu, 30 Nov 2017 23:54:03 -0500 Subject: Add ActiveStorage::Previewer#logger to match ActiveStorage::Analyzer#logger --- activestorage/lib/active_storage/previewer.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activestorage/lib/active_storage/previewer.rb b/activestorage/lib/active_storage/previewer.rb index ed75bae3b5..3d485988e9 100644 --- a/activestorage/lib/active_storage/previewer.rb +++ b/activestorage/lib/active_storage/previewer.rb @@ -54,5 +54,9 @@ module ActiveStorage IO.popen(argv) { |out| IO.copy_stream(out, to) } to.rewind end + + def logger + ActiveStorage.logger + end end end -- cgit v1.2.3 From 695ec1fef0b7fdb3125a3e2e4364a7f449265c1b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 1 Dec 2017 18:21:21 +0900 Subject: Class level `update` and `destroy` checks all the records exist before making changes (#31306) It makes more sense than ignoring invalid IDs. --- activerecord/lib/active_record/persistence.rb | 13 +++------ activerecord/test/cases/persistence_test.rb | 41 ++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 17459f2505..a13b0d0181 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -99,11 +99,9 @@ module ActiveRecord # for updating all records in a single query. def update(id = :all, attributes) if id.is_a?(Array) - id.map.with_index { |one_id, idx| - object = find_by(primary_key => one_id) - object.update(attributes[idx]) if object - object - }.compact + id.map { |one_id| find(one_id) }.each_with_index { |object, idx| + object.update(attributes[idx]) + } elsif id == :all all.each { |record| record.update(attributes) } else @@ -139,10 +137,7 @@ module ActiveRecord # Todo.destroy(todos) def destroy(id) if id.is_a?(Array) - id.map { |one_id| - object = find_by(primary_key => one_id) - object.destroy if object - }.compact + find(id).each(&:destroy) else find(id).destroy end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 643032e7cf..4aea275d51 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -70,7 +70,7 @@ class PersistenceTest < ActiveRecord::TestCase end def test_update_many - topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, nil => {} } + topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } updated = Topic.update(topic_data.keys, topic_data.values) assert_equal [1, 2], updated.map(&:id) @@ -78,10 +78,33 @@ class PersistenceTest < ActiveRecord::TestCase assert_equal "2 updated", Topic.find(2).content end + def test_update_many_with_duplicated_ids + updated = Topic.update([1, 1, 2], [ + { "content" => "1 duplicated" }, { "content" => "1 updated" }, { "content" => "2 updated" } + ]) + + assert_equal [1, 1, 2], updated.map(&:id) + assert_equal "1 updated", Topic.find(1).content + assert_equal "2 updated", Topic.find(2).content + end + + def test_update_many_with_invalid_id + topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, 99999 => {} } + + assert_raise(ActiveRecord::RecordNotFound) do + Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + end + + assert_not_equal "1 updated", Topic.find(1).content + assert_not_equal "2 updated", Topic.find(2).content + end + def test_class_level_update_is_affected_by_scoping topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } - assert_equal [], Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + assert_raise(ActiveRecord::RecordNotFound) do + Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + end assert_not_equal "1 updated", Topic.find(1).content assert_not_equal "2 updated", Topic.find(2).content @@ -175,15 +198,25 @@ class PersistenceTest < ActiveRecord::TestCase end def test_destroy_many - clients = Client.all.merge!(order: "id").find([2, 3]) + clients = Client.find([2, 3]) assert_difference("Client.count", -2) do - destroyed = Client.destroy([2, 3, nil]).sort_by(&:id) + destroyed = Client.destroy([2, 3]) assert_equal clients, destroyed assert destroyed.all?(&:frozen?), "destroyed clients should be frozen" end end + def test_destroy_many_with_invalid_id + clients = Client.find([2, 3]) + + assert_raise(ActiveRecord::RecordNotFound) do + Client.destroy([2, 3, 99999]) + end + + assert_equal clients, Client.find([2, 3]) + end + def test_becomes assert_kind_of Reply, topics(:first).becomes(Reply) assert_equal "The First Topic", topics(:first).becomes(Reply).title -- cgit v1.2.3 From 05d1e9e413a87bee5b0c1c200fa9f84dba0e1d15 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 1 Dec 2017 18:28:31 +0900 Subject: Remove unnecessary scoping --- activerecord/test/cases/persistence_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 4aea275d51..4cc66a2e49 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -92,7 +92,7 @@ class PersistenceTest < ActiveRecord::TestCase topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, 99999 => {} } assert_raise(ActiveRecord::RecordNotFound) do - Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + Topic.update(topic_data.keys, topic_data.values) end assert_not_equal "1 updated", Topic.find(1).content -- cgit v1.2.3 From 6b16f6db279bdbd95e8b295c9bab6760a6ff6af0 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 1 Dec 2017 20:42:04 +0900 Subject: Tweaks CHANGELOGs [ci skip] --- actionview/CHANGELOG.md | 2 +- activerecord/CHANGELOG.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 566e30993b..f42ada0baa 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,7 +1,7 @@ * Add `preload_link_tag` helper This helper that allows to the browser to initiate early fetch of resources - (different to the specified in javascript_include_tag and stylesheet_link_tag). + (different to the specified in `javascript_include_tag` and `stylesheet_link_tag`). Additionally, this sends Early Hints if supported by browser. *Guillermo Iguaran* diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 048a1eeb2f..89a12d4223 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -2,7 +2,7 @@ Example: - add_index :users, :name, using: :gist, opclass: name: :gist_trgm_ops + add_index :users, :name, using: :gist, opclass: { name: :gist_trgm_ops } *Greg Navis* @@ -80,7 +80,7 @@ *bogdanvlviv* * Fixed a bug where column orders for an index weren't written to - db/schema.rb when using the sqlite adapter. + `db/schema.rb` when using the sqlite adapter. Fixes #30902. -- cgit v1.2.3 From 32516e8862946da963e4ba9256553156ca321596 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 1 Dec 2017 20:48:29 +0900 Subject: Remove unpaired `}` [ci skip] --- .../active_record/connection_adapters/abstract/schema_statements.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 2a335e8f2b..7ae32d0ced 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -752,13 +752,12 @@ module ActiveRecord # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL # - # add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops }) + # add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops) # # generates: # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL # - # # Note: only supported by PostgreSQL # # ====== Creating an index with a specific type -- cgit v1.2.3 From 8203482a9ef9bd60d5014745fd7af868d9954b1d Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 20 Jan 2017 16:18:46 -0500 Subject: Add support for invalid foreign keys in Postgres Add validate_constraint and update naming --- .../abstract/schema_definitions.rb | 5 ++ .../abstract/schema_statements.rb | 2 + .../connection_adapters/abstract_adapter.rb | 5 ++ .../postgresql/schema_creation.rb | 12 ++++ .../postgresql/schema_definitions.rb | 13 +++++ .../postgresql/schema_statements.rb | 44 +++++++++++++- .../connection_adapters/postgresql_adapter.rb | 4 ++ .../test/cases/migration/foreign_key_test.rb | 68 ++++++++++++++++++++++ 8 files changed, 152 insertions(+), 1 deletion(-) 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 b66009bdc6..88ef8e0819 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -87,6 +87,11 @@ module ActiveRecord options[:primary_key] != default_primary_key end + def validate? + options.fetch(:validate, true) + end + alias validated? validate? + def defined_for?(to_table_ord = nil, to_table: nil, **options) if to_table_ord self.to_table == to_table_ord.to_s 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 7ae32d0ced..09ccf0c193 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -964,6 +964,8 @@ module ActiveRecord # Action that happens ON DELETE. Valid values are +:nullify+, +:cascade+ and +:restrict+ # [:on_update] # Action that happens ON UPDATE. Valid values are +:nullify+, +:cascade+ and +:restrict+ + # [:validate] + # (Postgres only) Specify whether or not the constraint should be validated. Defaults to +true+. def add_foreign_key(from_table, to_table, options = {}) return unless supports_foreign_keys? diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 8993c517a6..fc80d332f9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -272,6 +272,11 @@ module ActiveRecord false end + # Does this adapter support creating invalid constraints? + def supports_validate_constraints? + false + end + # Does this adapter support creating foreign key constraints # in the same statement as creating the table? def supports_foreign_keys_in_create? diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_creation.rb index 59f661da25..8e381a92cf 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_creation.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_creation.rb @@ -5,6 +5,18 @@ module ActiveRecord module PostgreSQL class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc: private + def visit_AlterTable(o) + super << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ") + end + + def visit_AddForeignKey(o) + super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? } + end + + def visit_ValidateConstraint(name) + "VALIDATE CONSTRAINT #{quote_column_name(name)}" + end + def add_column_options!(sql, options) if options[:collation] sql << " COLLATE \"#{options[:collation]}\"" diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb index 75622eb304..a3bb66bf3e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb @@ -192,6 +192,19 @@ module ActiveRecord class Table < ActiveRecord::ConnectionAdapters::Table include ColumnMethods end + + class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable + attr_reader :constraint_validations + + def initialize(td) + super + @constraint_validations = [] + end + + def validate_constraint(name) + @constraint_validations << name + 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 15a1dfd102..38102b2e7a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -507,7 +507,7 @@ module ActiveRecord def foreign_keys(table_name) scope = quoted_scope(table_name) fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA") - SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete + SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid FROM pg_constraint c JOIN pg_class t1 ON c.conrelid = t1.oid JOIN pg_class t2 ON c.confrelid = t2.oid @@ -529,6 +529,7 @@ module ActiveRecord options[:on_delete] = extract_foreign_key_action(row["on_delete"]) options[:on_update] = extract_foreign_key_action(row["on_update"]) + options[:validate] = row["valid"] ForeignKeyDefinition.new(table_name, row["to_table"], options) end @@ -589,6 +590,43 @@ module ActiveRecord PostgreSQL::SchemaDumper.create(self, options) end + # Validates the given constraint. + # + # Validates the constraint named +constraint_name+ on +accounts+. + # + # validate_foreign_key :accounts, :constraint_name + def validate_constraint(table_name, constraint_name) + return unless supports_validate_constraints? + + at = create_alter_table table_name + at.validate_constraint constraint_name + + execute schema_creation.accept(at) + end + + # Validates the given foreign key. + # + # Validates the foreign key on +accounts.branch_id+. + # + # validate_foreign_key :accounts, :branches + # + # Validates the foreign key on +accounts.owner_id+. + # + # validate_foreign_key :accounts, column: :owner_id + # + # Validates the foreign key named +special_fk_name+ on the +accounts+ table. + # + # validate_foreign_key :accounts, name: :special_fk_name + # + # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key. + def validate_foreign_key(from_table, options_or_to_table = {}) + return unless supports_validate_constraints? + + fk_name_to_validate = foreign_key_for!(from_table, options_or_to_table).name + + validate_constraint from_table, fk_name_to_validate + end + private def schema_creation PostgreSQL::SchemaCreation.new(self) @@ -598,6 +636,10 @@ module ActiveRecord PostgreSQL::TableDefinition.new(*args) end + def create_alter_table(name) + PostgreSQL::AlterTable.new create_table_definition(name) + end + def new_column_from_field(table_name, field) column_name, type, default, notnull, oid, fmod, collation, comment = field type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 9397481c4c..68b79f777e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -142,6 +142,10 @@ module ActiveRecord true end + def supports_validate_constraints? + true + end + def supports_views? true end diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb index 499d072de5..079be04946 100644 --- a/activerecord/test/cases/migration/foreign_key_test.rb +++ b/activerecord/test/cases/migration/foreign_key_test.rb @@ -227,6 +227,74 @@ if ActiveRecord::Base.connection.supports_foreign_keys? end end + if ActiveRecord::Base.connection.supports_validate_constraints? + def test_add_invalid_foreign_key + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false + + foreign_keys = @connection.foreign_keys("astronauts") + assert_equal 1, foreign_keys.size + + fk = foreign_keys.first + refute fk.validated? + end + + def test_validate_foreign_key_infers_column + @connection.add_foreign_key :astronauts, :rockets, validate: false + refute @connection.foreign_keys("astronauts").first.validated? + + @connection.validate_foreign_key :astronauts, :rockets + assert @connection.foreign_keys("astronauts").first.validated? + end + + def test_validate_foreign_key_by_column + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false + refute @connection.foreign_keys("astronauts").first.validated? + + @connection.validate_foreign_key :astronauts, column: "rocket_id" + assert @connection.foreign_keys("astronauts").first.validated? + end + + def test_validate_foreign_key_by_symbol_column + @connection.add_foreign_key :astronauts, :rockets, column: :rocket_id, validate: false + refute @connection.foreign_keys("astronauts").first.validated? + + @connection.validate_foreign_key :astronauts, column: :rocket_id + assert @connection.foreign_keys("astronauts").first.validated? + end + + def test_validate_foreign_key_by_name + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk", validate: false + refute @connection.foreign_keys("astronauts").first.validated? + + @connection.validate_foreign_key :astronauts, name: "fancy_named_fk" + assert @connection.foreign_keys("astronauts").first.validated? + end + + def test_validate_foreign_non_existing_foreign_key_raises + assert_raises ArgumentError do + @connection.validate_foreign_key :astronauts, :rockets + end + end + + def test_validate_constraint_by_name + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk", validate: false + + @connection.validate_constraint :astronauts, "fancy_named_fk" + assert @connection.foreign_keys("astronauts").first.validated? + end + else + # Foreign key should still be created, but should not be invalid + def test_add_invalid_foreign_key + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false + + foreign_keys = @connection.foreign_keys("astronauts") + assert_equal 1, foreign_keys.size + + fk = foreign_keys.first + assert fk.validated? + end + end + def test_schema_dumping @connection.add_foreign_key :astronauts, :rockets output = dump_table_schema "astronauts" -- cgit v1.2.3 From b852ef2660dac36e348865b455fab7fbcc0d2a7f Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Fri, 1 Dec 2017 11:07:30 -0500 Subject: Make ASt previewer/analyzer binary paths configurable --- .../lib/active_storage/analyzer/video_analyzer.rb | 4 +++- activestorage/lib/active_storage/engine.rb | 19 ++++++++++++++++++- .../lib/active_storage/previewer/pdf_previewer.rb | 9 ++++++++- .../lib/active_storage/previewer/video_previewer.rb | 4 +++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/activestorage/lib/active_storage/analyzer/video_analyzer.rb b/activestorage/lib/active_storage/analyzer/video_analyzer.rb index 408b5e58e9..b6fc54d917 100644 --- a/activestorage/lib/active_storage/analyzer/video_analyzer.rb +++ b/activestorage/lib/active_storage/analyzer/video_analyzer.rb @@ -19,6 +19,8 @@ module ActiveStorage # This analyzer requires the {ffmpeg}[https://www.ffmpeg.org] system library, which is not provided by Rails. You must # install ffmpeg yourself to use this analyzer. class Analyzer::VideoAnalyzer < Analyzer + class_attribute :ffprobe_path, default: "ffprobe" + def self.accept?(blob) blob.video? end @@ -68,7 +70,7 @@ module ActiveStorage end def probe_from(file) - IO.popen([ "ffprobe", "-print_format", "json", "-show_streams", "-v", "error", file.path ]) do |output| + IO.popen([ ffprobe_path, "-print_format", "json", "-show_streams", "-v", "error", file.path ]) do |output| JSON.parse(output.read) end rescue Errno::ENOENT diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb index 6cf6635c4f..b870e6d4d6 100644 --- a/activestorage/lib/active_storage/engine.rb +++ b/activestorage/lib/active_storage/engine.rb @@ -15,7 +15,8 @@ module ActiveStorage config.active_storage = ActiveSupport::OrderedOptions.new config.active_storage.previewers = [ ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ] - config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ] + config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ] + config.active_storage.paths = ActiveSupport::OrderedOptions.new config.eager_load_namespaces << ActiveStorage @@ -68,5 +69,21 @@ module ActiveStorage end end end + + initializer "active_storage.paths" do + config.after_initialize do |app| + if ffprobe_path = app.config.active_storage.paths.ffprobe + ActiveStorage::Analyzer::VideoAnalyzer.ffprobe_path = ffprobe_path + end + + if ffmpeg_path = app.config.active_storage.paths.ffmpeg + ActiveStorage::Previewer::VideoPreviewer.ffmpeg_path = ffmpeg_path + end + + if mutool_path = app.config.active_storage.paths.mutool + ActiveStorage::Previewer::PDFPreviewer.mutool_path = mutool_path + end + end + end end end diff --git a/activestorage/lib/active_storage/previewer/pdf_previewer.rb b/activestorage/lib/active_storage/previewer/pdf_previewer.rb index a2f05c74a6..b84aefcc9c 100644 --- a/activestorage/lib/active_storage/previewer/pdf_previewer.rb +++ b/activestorage/lib/active_storage/previewer/pdf_previewer.rb @@ -2,16 +2,23 @@ module ActiveStorage class Previewer::PDFPreviewer < Previewer + class_attribute :mutool_path, default: "mutool" + def self.accept?(blob) blob.content_type == "application/pdf" end def preview download_blob_to_tempfile do |input| - draw "mutool", "draw", "-F", "png", "-o", "-", input.path, "1" do |output| + draw_first_page_from input do |output| yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png" end end end + + private + def draw_first_page_from(file, &block) + draw mutool_path, "draw", "-F", "png", "-o", "-", file.path, "1", &block + end end end diff --git a/activestorage/lib/active_storage/previewer/video_previewer.rb b/activestorage/lib/active_storage/previewer/video_previewer.rb index 49f128d142..5d06e33f44 100644 --- a/activestorage/lib/active_storage/previewer/video_previewer.rb +++ b/activestorage/lib/active_storage/previewer/video_previewer.rb @@ -2,6 +2,8 @@ module ActiveStorage class Previewer::VideoPreviewer < Previewer + class_attribute :ffmpeg_path, default: "ffmpeg" + def self.accept?(blob) blob.video? end @@ -16,7 +18,7 @@ module ActiveStorage private def draw_relevant_frame_from(file, &block) - draw "ffmpeg", "-i", file.path, "-y", "-vcodec", "png", + draw ffmpeg_path, "-i", file.path, "-y", "-vcodec", "png", "-vf", "thumbnail", "-vframes", "1", "-f", "image2", "-", &block end end -- cgit v1.2.3 From 70c96b4423abacecb1116184c8ea4d4662b809f8 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sat, 2 Dec 2017 11:18:38 +0900 Subject: Fix method name in `validate_constraint` doc [ci skip] --- .../active_record/connection_adapters/postgresql/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 38102b2e7a..af13a9e16f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -594,7 +594,7 @@ module ActiveRecord # # Validates the constraint named +constraint_name+ on +accounts+. # - # validate_foreign_key :accounts, :constraint_name + # validate_constraint :accounts, :constraint_name def validate_constraint(table_name, constraint_name) return unless supports_validate_constraints? -- cgit v1.2.3 From dd6338a0699f2301d4b2fc8653688b4c4183cee5 Mon Sep 17 00:00:00 2001 From: Dinah Shi Date: Sat, 2 Dec 2017 14:30:10 +0800 Subject: Extract sql fragment generators for alter table from PostgreSQL adapter --- .../abstract/schema_statements.rb | 16 +++- .../connection_adapters/abstract_mysql_adapter.rb | 36 +++------ .../postgresql/schema_statements.rb | 86 +++++++++++++--------- .../lib/active_record/migration/compatibility.rb | 9 +++ .../test/cases/adapters/mysql2/connection_test.rb | 4 +- .../test/cases/migration/compatibility_test.rb | 17 +++++ 6 files changed, 107 insertions(+), 61 deletions(-) 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 9b7345f7c3..f0c6ad63f9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -600,7 +600,7 @@ module ActiveRecord # to provide these in a migration's +change+ method so it can be reverted. # In that case, +type+ and +options+ will be used by #add_column. def remove_column(table_name, column_name, type = nil, options = {}) - execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}" + execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}" end # Changes the column's definition according to the new options. @@ -1340,6 +1340,20 @@ module ActiveRecord options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty? end + def add_column_for_alter(table_name, column_name, type, options = {}) + td = create_table_definition(table_name) + cd = td.new_column_definition(column_name, type, options) + schema_creation.accept(AddColumnDefinition.new(cd)) + end + + def remove_column_for_alter(table_name, column_name, type = nil, options = {}) + "DROP COLUMN #{quote_column_name(column_name)}" + end + + def remove_columns_for_alter(table_name, *column_names) + column_names.map { |column_name| remove_column_for_alter(table_name, column_name) } + end + def insert_versions_sql(versions) sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_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 0e552dba95..2926366e10 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -299,7 +299,7 @@ module ActiveRecord def bulk_change_table(table_name, operations) #:nodoc: sqls = operations.flat_map do |command, args| table, arguments = args.shift, args - method = :"#{command}_sql" + method = :"#{command}_for_alter" if respond_to?(method, true) send(method, table, *arguments) @@ -372,11 +372,11 @@ module ActiveRecord end def change_column(table_name, column_name, type, options = {}) #:nodoc: - execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}") + execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}") end def rename_column(table_name, column_name, new_column_name) #:nodoc: - execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}") + execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}") rename_column_indexes(table_name, column_name, new_column_name) end @@ -668,13 +668,7 @@ module ActiveRecord end end - def add_column_sql(table_name, column_name, type, options = {}) - td = create_table_definition(table_name) - cd = td.new_column_definition(column_name, type, options) - schema_creation.accept(AddColumnDefinition.new(cd)) - end - - def change_column_sql(table_name, column_name, type, options = {}) + def change_column_for_alter(table_name, column_name, type, options = {}) column = column_for(table_name, column_name) type ||= column.sql_type @@ -695,7 +689,7 @@ module ActiveRecord schema_creation.accept(ChangeColumnDefinition.new(cd, column.name)) end - def rename_column_sql(table_name, column_name, new_column_name) + def rename_column_for_alter(table_name, column_name, new_column_name) column = column_for(table_name, column_name) options = { default: column.default, @@ -709,31 +703,23 @@ module ActiveRecord schema_creation.accept(ChangeColumnDefinition.new(cd, column.name)) end - def remove_column_sql(table_name, column_name, type = nil, options = {}) - "DROP #{quote_column_name(column_name)}" - end - - def remove_columns_sql(table_name, *column_names) - column_names.map { |column_name| remove_column_sql(table_name, column_name) } - end - - def add_index_sql(table_name, column_name, options = {}) + def add_index_for_alter(table_name, column_name, options = {}) index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options) index_algorithm[0, 0] = ", " if index_algorithm.present? "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}" end - def remove_index_sql(table_name, options = {}) + def remove_index_for_alter(table_name, options = {}) index_name = index_name_for_remove(table_name, options) "DROP INDEX #{quote_column_name(index_name)}" end - def add_timestamps_sql(table_name, options = {}) - [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)] + def add_timestamps_for_alter(table_name, options = {}) + [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)] end - def remove_timestamps_sql(table_name, options = {}) - [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)] + def remove_timestamps_for_alter(table_name, options = {}) + [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)] end # MySQL is too stupid to create a temporary table for use subquery, so we have 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 846e721983..371597031c 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -391,51 +391,24 @@ module ActiveRecord end def change_column(table_name, column_name, type, options = {}) #:nodoc: - clear_cache! quoted_table_name = quote_table_name(table_name) - quoted_column_name = quote_column_name(column_name) - sql_type = type_to_sql(type, options) - sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}".dup - if options[:collation] - sql << " COLLATE \"#{options[:collation]}\"" - end - if options[:using] - sql << " USING #{options[:using]}" - elsif options[:cast_as] - cast_as_type = type_to_sql(options[:cast_as], options) - sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})" - end - execute sql - - change_column_default(table_name, column_name, options[:default]) if options.key?(:default) - change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null) - change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment) + sqls, procs = change_column_for_alter(table_name, column_name, type, options) + execute "ALTER TABLE #{quoted_table_name} #{sqls.join(", ")}" + procs.each(&:call) end # Changes the default value of a table column. def change_column_default(table_name, column_name, default_or_changes) # :nodoc: - clear_cache! - column = column_for(table_name, column_name) - return unless column - - default = extract_new_default_value(default_or_changes) - alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s" - if default.nil? - # DEFAULT NULL results in the same behavior as DROP DEFAULT. However, PostgreSQL will - # cast the default to the columns type, which leaves us with a default like "default NULL::character varying". - execute alter_column_query % "DROP DEFAULT" - else - execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}" - end + execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}" end def change_column_null(table_name, column_name, null, default = nil) #:nodoc: clear_cache! unless null || default.nil? column = column_for(table_name, column_name) - execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column + execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL" if column end - execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL") + execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}" end # Adds comment for given table column or drops it if +comment+ is a +nil+ @@ -629,6 +602,53 @@ module ActiveRecord end end + def change_column_for_alter(table_name, column_name, type, options = {}) + clear_cache! + quoted_column_name = quote_column_name(column_name) + sql_type = type_to_sql(type, options) + sql = "ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}".dup + if options[:collation] + sql << " COLLATE \"#{options[:collation]}\"" + end + if options[:using] + sql << " USING #{options[:using]}" + elsif options[:cast_as] + cast_as_type = type_to_sql(options[:cast_as], options) + sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})" + end + + sqls = [sql] + procs = [] + sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default) + sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null) + procs << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment) + + [sqls, procs] + end + + + # Changes the default value of a table column. + def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc: + clear_cache! + column = column_for(table_name, column_name) + return unless column + + default = extract_new_default_value(default_or_changes) + alter_column_query = "ALTER COLUMN #{quote_column_name(column_name)} %s" + if default.nil? + # DEFAULT NULL results in the same behavior as DROP DEFAULT. However, PostgreSQL will + # cast the default to the columns type, which leaves us with a default like "default NULL::character varying". + alter_column_query % "DROP DEFAULT" + else + alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}" + end + end + + def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc: + clear_cache! + "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL" + end + def data_source_sql(name = nil, type: nil) scope = quoted_scope(name, type: type) scope[:type] ||= "'r','v','m'" # (r)elation/table, (v)iew, (m)aterialized view diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index c979aaf0a0..da307c1723 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -16,6 +16,15 @@ module ActiveRecord V5_2 = Current class V5_1 < V5_2 + if adapter_name == "PostgreSQL" + def change_column(table_name, column_name, type, options = {}) + unless options[:null] || options[:default].nil? + column = connection.send(:column_for, table_name, column_name) + connection.execute("UPDATE #{connection.quote_table_name(table_name)} SET #{connection.quote_column_name(column_name)}=#{connection.quote_default_expression(options[:default], column)} WHERE #{connection.quote_column_name(column_name)} IS NULL") if column + end + connection.change_column(table_name, column_name, type, options) + end + end end class V5_0 < V5_1 diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index e61c70848a..13b4096671 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -174,10 +174,10 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase assert_equal "SCHEMA", @subscriber.logged[0][1] end - def test_logs_name_rename_column_sql + def test_logs_name_rename_column_for_alter @connection.execute "CREATE TABLE `bar_baz` (`foo` varchar(255))" @subscriber.logged.clear - @connection.send(:rename_column_sql, "bar_baz", "foo", "foo2") + @connection.send(:rename_column_for_alter, "bar_baz", "foo", "foo2") assert_equal "SCHEMA", @subscriber.logged[0][1] ensure @connection.execute "DROP TABLE `bar_baz`" diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 2fef2f796e..0fd55e6ae4 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -126,6 +126,23 @@ module ActiveRecord end assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message) end + + if current_adapter?(:PostgreSQLAdapter) + class Testing < ActiveRecord::Base + end + + def test_legacy_change_column_with_null_executes_update + migration = Class.new(ActiveRecord::Migration[5.1]) { + def migrate(x) + change_column :testings, :foo, :string, null: false, default: "foobar" + end + }.new + + t = Testing.create! + ActiveRecord::Migrator.new(:up, [migration]).migrate + assert_equal ["foobar"], Testing.all.map(&:foo) + end + end end end end -- cgit v1.2.3 From 99f6722e86c5451e1334db31a18ae2cbaccb551d Mon Sep 17 00:00:00 2001 From: willnet Date: Sat, 2 Dec 2017 16:35:38 +0900 Subject: [ci skip] Add a missing space before closing curly braces --- guides/source/form_helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index 4ce67df93a..f8ec389b01 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -920,7 +920,7 @@ When an association accepts nested attributes `fields_for` renders its block onc ```ruby def new @person = Person.new - 2.times { @person.addresses.build} + 2.times { @person.addresses.build } end ``` -- cgit v1.2.3 From a71bbed76aab9e8a9a6b2da18bcacd8ee32a0dd0 Mon Sep 17 00:00:00 2001 From: claudiob Date: Sat, 2 Dec 2017 12:00:09 -0800 Subject: Fix typo in test error message With the current code, a failing test shows this error, which is missing the number of times called and has two periods at the end. ``` /railties$ be ruby -Itest test/generators/app_generator_test.rb -n test_active_storage_install Failure: AppGeneratorTest#test_active_storage_install [test/generators/app_generator_test.rb:313]: active_storage:install expected to be called once, but was called times.. Expected: 1 Actual: 2 ``` After the fix, the error message looks correct: ``` /railties$ be ruby -Itest test/generators/app_generator_test.rb -n test_active_storage_install Failure: AppGeneratorTest#test_active_storage_install [test/generators/app_generator_test.rb:313]: active_storage:install expected to be called once, but was called 2 times. Expected: 1 Actual: 2 ``` --- railties/test/generators/app_generator_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 774fd0f315..8d12f4e5a7 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -310,7 +310,7 @@ class AppGeneratorTest < Rails::Generators::TestCase case command when "active_storage:install" @binstub_called += 1 - assert_equal 1, @binstub_called, "active_storage:install expected to be called once, but was called #{@install_called} times." + assert_equal 1, @binstub_called, "active_storage:install expected to be called once, but was called #{@binstub_called} times" end end -- cgit v1.2.3 From 01fb0907b2dafcecf63eedb80408d4cbff5f4d23 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 04:57:04 +0900 Subject: Refactor `length`, `order`, and `opclass` index options dumping --- .../abstract/schema_definitions.rb | 19 ++++++++++++----- .../abstract/schema_statements.rb | 2 +- .../connection_adapters/abstract_mysql_adapter.rb | 2 +- .../connection_adapters/mysql/schema_statements.rb | 15 ++++++++------ .../postgresql/schema_statements.rb | 16 +++++---------- activerecord/lib/active_record/schema_dumper.rb | 14 ++++++++++--- .../test/cases/adapters/postgresql/schema_test.rb | 6 +++--- activerecord/test/cases/schema_dumper_test.rb | 24 +++++++++++++++++++--- activerecord/test/schema/schema.rb | 2 ++ 9 files changed, 67 insertions(+), 33 deletions(-) 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 88ef8e0819..86406d95ad 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -6,7 +6,7 @@ module ActiveRecord # this type are typically created and returned by methods in database # adapters. e.g. ActiveRecord::ConnectionAdapters::MySQL::SchemaStatements#indexes class IndexDefinition # :nodoc: - attr_reader :table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :opclass, :comment + attr_reader :table, :name, :unique, :columns, :lengths, :orders, :opclasses, :where, :type, :using, :comment def initialize( table, name, @@ -14,24 +14,33 @@ module ActiveRecord columns = [], lengths: {}, orders: {}, + opclasses: {}, where: nil, type: nil, using: nil, - opclass: {}, comment: nil ) @table = table @name = name @unique = unique @columns = columns - @lengths = lengths - @orders = orders + @lengths = concise_options(lengths) + @orders = concise_options(orders) + @opclasses = concise_options(opclasses) @where = where @type = type @using = using - @opclass = opclass @comment = comment end + + private + def concise_options(options) + if columns.size == options.size && options.values.uniq.size == 1 + options.values.first + else + options + end + end end # Abstract representation of a column definition. Instances of this type 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 fac9e1cca2..efc934c34c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1202,7 +1202,7 @@ module ActiveRecord when Hash order = order.symbolize_keys quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? } - when String + else quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? } end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 143dc6d08d..41c628302d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -611,7 +611,7 @@ module ActiveRecord when Hash length = length.symbolize_keys quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? } - when Integer + else quoted_columns.each { |name, column| column << "(#{length})" } end end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb index a15c7d1787..d5b2c18ee6 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb @@ -22,23 +22,26 @@ module ActiveRecord index_using = mysql_index_type end - indexes << IndexDefinition.new( + indexes << [ row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, + [], + lengths: {}, + orders: {}, type: index_type, using: index_using, comment: row[:Index_comment].presence - ) + ] end - indexes.last.columns << row[:Column_name] - indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part] - indexes.last.orders.merge!(row[:Column_name] => :desc) if row[:Collation] == "D" + indexes.last[-2] << row[:Column_name] + indexes.last[-1][:lengths].merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part] + indexes.last[-1][:orders].merge!(row[:Column_name] => :desc) if row[:Collation] == "D" end end - indexes + indexes.map { |index| IndexDefinition.new(*index) } end def remove_column(table_name, column_name, type = nil, options = {}) 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 64f08e0ee1..7b4d5711cb 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -109,6 +109,9 @@ module ActiveRecord using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten + orders = {} + opclasses = {} + if indkey.include?(0) columns = expressions else @@ -119,21 +122,12 @@ module ActiveRecord AND a.attnum IN (#{indkey.join(",")}) SQL - orders = {} - opclasses = {} - # add info on sort order (only desc order is explicitly specified, asc is the default) # and non-default opclasses expressions.scan(/(\w+)(?: (?!DESC)(\w+))?(?: (DESC))?/).each do |column, opclass, desc| - opclasses[column] = opclass if opclass + opclasses[column] = opclass.to_sym if opclass orders[column] = :desc if desc end - - # Use a string for the opclass description (instead of a hash) when all columns - # have the same opclass specified. - if columns.count == opclasses.count && opclasses.values.uniq.count == 1 - opclasses = opclasses.values.first - end end IndexDefinition.new( @@ -142,9 +136,9 @@ module ActiveRecord unique, columns, orders: orders, + opclasses: opclasses, where: where, using: using.to_sym, - opclass: opclasses, comment: comment.presence ) end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 8cb2851557..16ccba6b6c 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -184,12 +184,12 @@ HEADER "name: #{index.name.inspect}", ] index_parts << "unique: true" if index.unique - index_parts << "length: { #{format_options(index.lengths)} }" if index.lengths.present? - index_parts << "order: { #{format_options(index.orders)} }" if index.orders.present? + index_parts << "length: #{format_index_parts(index.lengths)}" if index.lengths.present? + index_parts << "order: #{format_index_parts(index.orders)}" if index.orders.present? + index_parts << "opclass: #{format_index_parts(index.opclasses)}" if index.opclasses.present? index_parts << "where: #{index.where.inspect}" if index.where index_parts << "using: #{index.using.inspect}" if !@connection.default_index_type?(index) index_parts << "type: #{index.type.inspect}" if index.type - index_parts << "opclass: #{index.opclass.inspect}" if index.opclass.present? index_parts << "comment: #{index.comment.inspect}" if index.comment index_parts end @@ -232,6 +232,14 @@ HEADER options.map { |key, value| "#{key}: #{value.inspect}" }.join(", ") end + def format_index_parts(options) + if options.is_a?(Hash) + "{ #{format_options(options)} }" + else + options.inspect + end + end + def remove_prefix_and_suffix(table) prefix = Regexp.escape(@options[:table_name_prefix].to_s) suffix = Regexp.escape(@options[:table_name_suffix].to_s) diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index a6ae18a826..1126908761 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -459,7 +459,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase assert_equal :btree, index_d.using assert_equal :gin, index_e.using - assert_equal :desc, index_d.orders[INDEX_D_COLUMN] + assert_equal :desc, index_d.orders end end @@ -520,7 +520,7 @@ class SchemaIndexOpclassTest < ActiveRecord::PostgreSQLTestCase output = dump_table_schema "trains" - assert_match(/opclass: "text_pattern_ops"/, output) + assert_match(/opclass: :text_pattern_ops/, output) end def test_non_default_opclass_is_dumped @@ -528,7 +528,7 @@ class SchemaIndexOpclassTest < ActiveRecord::PostgreSQLTestCase output = dump_table_schema "trains" - assert_match(/opclass: \{"description"=>"text_pattern_ops"\}/, output) + assert_match(/opclass: \{ description: :text_pattern_ops \}/, output) end end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index ac5092f1c1..a612ce9bb2 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -177,14 +177,14 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_schema_dumps_index_columns_in_right_order index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_index/).first.strip - if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) - assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition - elsif current_adapter?(:Mysql2Adapter) + if current_adapter?(:Mysql2Adapter) if ActiveRecord::Base.connection.supports_index_sort_order? assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }, order: { rating: :desc }', index_definition else assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }', index_definition end + elsif ActiveRecord::Base.connection.supports_index_sort_order? + assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition else assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition end @@ -199,6 +199,24 @@ class SchemaDumperTest < ActiveRecord::TestCase end end + def test_schema_dumps_index_sort_order + index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*_name_and_rating/).first.strip + if ActiveRecord::Base.connection.supports_index_sort_order? + assert_equal 't.index ["name", "rating"], name: "index_companies_on_name_and_rating", order: :desc', index_definition + else + assert_equal 't.index ["name", "rating"], name: "index_companies_on_name_and_rating"', index_definition + end + end + + def test_schema_dumps_index_length + index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*_name_and_description/).first.strip + if current_adapter?(:Mysql2Adapter) + assert_equal 't.index ["name", "description"], name: "index_companies_on_name_and_description", length: 10', index_definition + else + assert_equal 't.index ["name", "description"], name: "index_companies_on_name_and_description"', index_definition + end + end + def test_schema_dump_should_honor_nonstandard_primary_keys output = standard_dump match = output.match(%r{create_table "movies"(.*)do}) diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index a4505a4892..bf66846840 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -205,6 +205,8 @@ ActiveRecord::Schema.define do t.bigint :rating, default: 1 t.integer :account_id t.string :description, default: "" + t.index [:name, :rating], order: :desc + t.index [:name, :description], length: 10 t.index [:firm_id, :type, :rating], name: "company_index", length: { type: 10 }, order: { rating: :desc } t.index [:firm_id, :type], name: "company_partial_index", where: "(rating > 10)" t.index :name, name: "company_name_index", using: :btree -- cgit v1.2.3 From 3040446cece8e7a6d9e29219e636e13f180a1e03 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 06:33:56 +0900 Subject: Fix warning: assigned but unused variable - t --- activerecord/test/cases/migration/compatibility_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 0fd55e6ae4..f1defe1a8a 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -138,7 +138,7 @@ module ActiveRecord end }.new - t = Testing.create! + Testing.create! ActiveRecord::Migrator.new(:up, [migration]).migrate assert_equal ["foobar"], Testing.all.map(&:foo) end -- cgit v1.2.3 From 33a3f7123bc4cc49b99b01a40bbfd463b2e73f76 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 07:08:30 +0900 Subject: Extract duplicated index column options normalization as `options_for_index_columns` And placed `add_options_for_index_columns` in `schema_statements.rb` consistently to ease to find related code. --- .../connection_adapters/abstract/schema_statements.rb | 19 ++++++++++--------- .../connection_adapters/abstract_mysql_adapter.rb | 19 ------------------- .../connection_adapters/mysql/schema_statements.rb | 12 ++++++++++++ .../postgresql/schema_statements.rb | 12 ++++++++++++ .../connection_adapters/postgresql_adapter.rb | 18 ------------------ 5 files changed, 34 insertions(+), 46 deletions(-) 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 efc934c34c..4f58b0242c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1197,17 +1197,18 @@ module ActiveRecord end def add_index_sort_order(quoted_columns, **options) - if order = options[:order] - case order - when Hash - order = order.symbolize_keys - quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? } - else - quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? } - end + orders = options_for_index_columns(options[:order]) + quoted_columns.each do |name, column| + column << " #{orders[name].upcase}" if orders[name].present? end + end - quoted_columns + def options_for_index_columns(options) + if options.is_a?(Hash) + options.symbolize_keys + else + Hash.new { |hash, column| hash[column] = options } + end end # Overridden by the MySQL adapter for supporting index lengths and by 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 41c628302d..479131caad 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -605,25 +605,6 @@ module ActiveRecord end end - def add_index_length(quoted_columns, **options) - if length = options[:length] - case length - when Hash - length = length.symbolize_keys - quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? } - else - quoted_columns.each { |name, column| column << "(#{length})" } - end - end - - quoted_columns - end - - def add_options_for_index_columns(quoted_columns, **options) - quoted_columns = add_index_length(quoted_columns, options) - super - end - # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html ER_DUP_ENTRY = 1062 ER_NOT_NULL_VIOLATION = 1048 diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb index d5b2c18ee6..ce50590651 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb @@ -106,6 +106,18 @@ module ActiveRecord super unless specifier == "RESTRICT" end + def add_index_length(quoted_columns, **options) + lengths = options_for_index_columns(options[:length]) + quoted_columns.each do |name, column| + column << "(#{lengths[name]})" if lengths[name].present? + end + end + + def add_options_for_index_columns(quoted_columns, **options) + quoted_columns = add_index_length(quoted_columns, options) + super + end + def data_source_sql(name = nil, type: nil) scope = quoted_scope(name, type: type) 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 7b4d5711cb..2e9e74403b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -693,6 +693,18 @@ module ActiveRecord "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL" end + def add_index_opclass(quoted_columns, **options) + opclasses = options_for_index_columns(options[:opclass]) + quoted_columns.each do |name, column| + column << " #{opclasses[name]}" if opclasses[name].present? + end + end + + def add_options_for_index_columns(quoted_columns, **options) + quoted_columns = add_index_opclass(quoted_columns, options) + super + end + def data_source_sql(name = nil, type: nil) scope = quoted_scope(name, type: type) scope[:type] ||= "'r','v','m'" # (r)elation/table, (v)iew, (m)aterialized view diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 68b79f777e..23fc69d649 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -390,24 +390,6 @@ module ActiveRecord end private - - def add_index_opclass(column_names, options = {}) - opclass = - if options[:opclass].is_a?(Hash) - options[:opclass].symbolize_keys - else - Hash.new { |hash, column| hash[column] = options[:opclass].to_s } - end - column_names.each do |name, column| - column << " #{opclass[name]}" if opclass[name].present? - end - end - - def add_options_for_index_columns(quoted_columns, **options) - quoted_columns = add_index_opclass(quoted_columns, options) - super - end - # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html VALUE_LIMIT_VIOLATION = "22001" NUMERIC_VALUE_OUT_OF_RANGE = "22003" -- cgit v1.2.3 From 5358f2b67bd6fb12d708527a4a70dcab65513c5e Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 07:55:34 +0900 Subject: Fix `test_add_column_with_timestamp_type` failure This test failed due to dirty schema cache. It is needed to call `clear_cache!` when using same named table with different definition. https://travis-ci.org/rails/rails/jobs/310627767#L769-L772 --- activerecord/test/cases/migration/change_schema_test.rb | 13 ++++++------- activerecord/test/cases/migration/compatibility_test.rb | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 7b0644e9c0..43aac5da75 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -264,19 +264,18 @@ module ActiveRecord t.column :foo, :timestamp end - klass = Class.new(ActiveRecord::Base) - klass.table_name = "testings" + column = connection.columns(:testings).find { |c| c.name == "foo" } - assert_equal :datetime, klass.columns_hash["foo"].type + assert_equal :datetime, column.type if current_adapter?(:PostgreSQLAdapter) - assert_equal "timestamp without time zone", klass.columns_hash["foo"].sql_type + assert_equal "timestamp without time zone", column.sql_type elsif current_adapter?(:Mysql2Adapter) - assert_equal "timestamp", klass.columns_hash["foo"].sql_type + assert_equal "timestamp", column.sql_type elsif current_adapter?(:OracleAdapter) - assert_equal "TIMESTAMP(6)", klass.columns_hash["foo"].sql_type + assert_equal "TIMESTAMP(6)", column.sql_type else - assert_equal klass.connection.type_to_sql("datetime"), klass.columns_hash["foo"].sql_type + assert_equal klass.connection.type_to_sql("datetime"), column.sql_type end end diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index f1defe1a8a..e6ca252369 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -141,6 +141,8 @@ module ActiveRecord Testing.create! ActiveRecord::Migrator.new(:up, [migration]).migrate assert_equal ["foobar"], Testing.all.map(&:foo) + ensure + ActiveRecord::Base.clear_cache! end end end -- cgit v1.2.3 From 32516983a67f10d513370edd67b41ef0e011b175 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 08:05:32 +0900 Subject: Fix `s/klass.connection/connection/` `klass` has removed in 5358f2b67bd6fb12d708527a4a70dcab65513c5e. --- activerecord/test/cases/migration/change_schema_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 43aac5da75..38a906c8f5 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -275,7 +275,7 @@ module ActiveRecord elsif current_adapter?(:OracleAdapter) assert_equal "TIMESTAMP(6)", column.sql_type else - assert_equal klass.connection.type_to_sql("datetime"), column.sql_type + assert_equal connection.type_to_sql("datetime"), column.sql_type end end -- cgit v1.2.3 From dbee80bca0ef504120219e6c7686437456511060 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Fri, 1 Dec 2017 18:16:06 +0900 Subject: Make `Migrator.current_version` work without a current database This is necessary in order to make the processing dependent on `Migrator.current_version` work even without database. Context: https://github.com/rails/rails/pull/31135#issuecomment-348404326 --- activerecord/lib/active_record/migration.rb | 10 +++++++++- activerecord/lib/active_record/railtie.rb | 7 +++++-- railties/test/application/rake/dbs_test.rb | 14 ++++++++++++++ railties/test/isolation/abstract_unit.rb | 15 +++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 15e9c09ffb..4d4b0dc67a 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1053,7 +1053,15 @@ module ActiveRecord end end - def current_version(connection = Base.connection) + def current_version(connection = nil) + if connection.nil? + begin + connection = Base.connection + rescue ActiveRecord::NoDatabaseError + return nil + end + end + get_all_versions(connection).max || 0 end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 9ee8425e1b..4538ed6a5f 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -90,12 +90,15 @@ module ActiveRecord filename = File.join(app.config.paths["db"].first, "schema_cache.yml") if File.file?(filename) + current_version = ActiveRecord::Migrator.current_version + next if current_version.nil? + cache = YAML.load(File.read(filename)) - if cache.version == ActiveRecord::Migrator.current_version + if cache.version == current_version 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}." + warn "Ignoring db/schema_cache.yml because it has expired. The current schema version is #{current_version}, but the one in the cache is #{cache.version}." end end end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 0235210fdd..2082e9fa9f 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -98,6 +98,20 @@ module ApplicationTests end end + test "db:create works when schema cache exists and database does not exist" do + use_postgresql + + begin + rails %w(db:create db:migrate db:schema:cache:dump) + + rails "db:drop" + rails "db:create" + assert_equal 0, $?.exitstatus + ensure + rails "db:drop" rescue nil + end + end + test "db:drop failure because database does not exist" do output = rails("db:drop:_unsafe", "--trace") assert_match(/does not exist/, output) diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 7522237a38..5b1c06d4e5 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -381,6 +381,21 @@ module TestHelpers $:.reject! { |path| path =~ %r'/(#{to_remove.join('|')})/' } end + + def use_postgresql + File.open("#{app_path}/config/database.yml", "w") do |f| + f.puts <<-YAML + default: &default + adapter: postgresql + pool: 5 + database: railties_test + development: + <<: *default + test: + <<: *default + YAML + end + end end end -- cgit v1.2.3 From 8c5a7fbefd3cad403e7594d0b6a5488d80d4c98e Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Sat, 2 Dec 2017 22:43:28 -0500 Subject: Purge variants with their blobs --- activestorage/app/models/active_storage/blob.rb | 3 ++- activestorage/lib/active_storage/log_subscriber.rb | 4 +++ activestorage/lib/active_storage/service.rb | 9 +++++-- .../service/azure_storage_service.rb | 30 +++++++++++++++++----- .../lib/active_storage/service/disk_service.rb | 22 +++++++++++----- .../lib/active_storage/service/gcs_service.rb | 18 ++++++++----- .../lib/active_storage/service/mirror_service.rb | 5 ++++ .../lib/active_storage/service/s3_service.rb | 20 ++++++++++----- activestorage/test/models/blob_test.rb | 10 +++++++- activestorage/test/service/shared_service_tests.rb | 17 ++++++++++++ 10 files changed, 107 insertions(+), 31 deletions(-) diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 2aa05d665e..acaf22fac1 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -270,7 +270,8 @@ class ActiveStorage::Blob < ActiveRecord::Base # deleted as well or you will essentially have a dead reference. It's recommended to use the +#purge+ and +#purge_later+ # methods in most circumstances. def delete - service.delete key + service.delete(key) + service.delete_prefixed("variants/#{key}/") if image? end # Deletes the file on the service and then destroys the blob record. This is the recommended way to dispose of unwanted diff --git a/activestorage/lib/active_storage/log_subscriber.rb b/activestorage/lib/active_storage/log_subscriber.rb index 5cbf4bd1a5..a4e148c1a5 100644 --- a/activestorage/lib/active_storage/log_subscriber.rb +++ b/activestorage/lib/active_storage/log_subscriber.rb @@ -18,6 +18,10 @@ module ActiveStorage info event, color("Deleted file from key: #{key_in(event)}", RED) end + def service_delete_prefixed(event) + info event, color("Deleted files by key prefix: #{event.payload[:prefix]}", RED) + end + def service_exist(event) debug event, color("Checked if file exists at key: #{key_in(event)} (#{event.payload[:exist] ? "yes" : "no"})", BLUE) end diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb index aa150e4d8a..c8f675db86 100644 --- a/activestorage/lib/active_storage/service.rb +++ b/activestorage/lib/active_storage/service.rb @@ -78,6 +78,11 @@ module ActiveStorage raise NotImplementedError end + # Delete files at keys starting with the +prefix+. + def delete_prefixed(prefix) + raise NotImplementedError + end + # Return +true+ if a file exists at the +key+. def exist?(key) raise NotImplementedError @@ -104,10 +109,10 @@ module ActiveStorage end private - def instrument(operation, key, payload = {}, &block) + def instrument(operation, payload = {}, &block) ActiveSupport::Notifications.instrument( "service_#{operation}.active_storage", - payload.merge(key: key, service: service_name), &block) + payload.merge(service: service_name), &block) end def service_name diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb index f3877ad9c9..98de0836de 100644 --- a/activestorage/lib/active_storage/service/azure_storage_service.rb +++ b/activestorage/lib/active_storage/service/azure_storage_service.rb @@ -19,7 +19,7 @@ module ActiveStorage end def upload(key, io, checksum: nil) - instrument :upload, key, checksum: checksum do + instrument :upload, key: key, checksum: checksum do begin blobs.create_block_blob(container, key, io, content_md5: checksum) rescue Azure::Core::Http::HTTPError @@ -30,11 +30,11 @@ module ActiveStorage def download(key, &block) if block_given? - instrument :streaming_download, key do + instrument :streaming_download, key: key do stream(key, &block) end else - instrument :download, key do + instrument :download, key: key do _, io = blobs.get_blob(container, key) io.force_encoding(Encoding::BINARY) end @@ -42,7 +42,7 @@ module ActiveStorage end def delete(key) - instrument :delete, key do + instrument :delete, key: key do begin blobs.delete_blob(container, key) rescue Azure::Core::Http::HTTPError @@ -51,8 +51,24 @@ module ActiveStorage end end + def delete_prefixed(prefix) + instrument :delete_all, prefix: prefix do + marker = nil + + loop do + results = blobs.list_blobs(container, prefix: prefix, marker: marker) + + results.each do |blob| + blobs.delete_blob(container, blob.name) + end + + break unless marker = results.continuation_token.presence + end + end + end + def exist?(key) - instrument :exist, key do |payload| + instrument :exist, key: key do |payload| answer = blob_for(key).present? payload[:exist] = answer answer @@ -60,7 +76,7 @@ module ActiveStorage end def url(key, expires_in:, filename:, disposition:, content_type:) - instrument :url, key do |payload| + instrument :url, key: key do |payload| base_url = url_for(key) generated_url = signer.signed_uri( URI(base_url), false, @@ -77,7 +93,7 @@ module ActiveStorage end def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) - instrument :url, key do |payload| + instrument :url, key: key do |payload| base_url = url_for(key) generated_url = signer.signed_uri(URI(base_url), false, permissions: "rw", expiry: format_expiry(expires_in)).to_s diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb index 52eaba4e7b..a8728c5bc3 100644 --- a/activestorage/lib/active_storage/service/disk_service.rb +++ b/activestorage/lib/active_storage/service/disk_service.rb @@ -16,7 +16,7 @@ module ActiveStorage end def upload(key, io, checksum: nil) - instrument :upload, key, checksum: checksum do + instrument :upload, key: key, checksum: checksum do IO.copy_stream(io, make_path_for(key)) ensure_integrity_of(key, checksum) if checksum end @@ -24,7 +24,7 @@ module ActiveStorage def download(key) if block_given? - instrument :streaming_download, key do + instrument :streaming_download, key: key do File.open(path_for(key), "rb") do |file| while data = file.read(64.kilobytes) yield data @@ -32,14 +32,14 @@ module ActiveStorage end end else - instrument :download, key do + instrument :download, key: key do File.binread path_for(key) end end end def delete(key) - instrument :delete, key do + instrument :delete, key: key do begin File.delete path_for(key) rescue Errno::ENOENT @@ -48,8 +48,16 @@ module ActiveStorage end end + def delete_prefixed(prefix) + instrument :delete_prefixed, prefix: prefix do + Dir.glob(path_for("#{prefix}*")).each do |path| + FileUtils.rm_rf(path) + end + end + end + def exist?(key) - instrument :exist, key do |payload| + instrument :exist, key: key do |payload| answer = File.exist? path_for(key) payload[:exist] = answer answer @@ -57,7 +65,7 @@ module ActiveStorage end def url(key, expires_in:, filename:, disposition:, content_type:) - instrument :url, key do |payload| + instrument :url, key: key do |payload| verified_key_with_expiration = ActiveStorage.verifier.generate(key, expires_in: expires_in, purpose: :blob_key) generated_url = @@ -77,7 +85,7 @@ module ActiveStorage end def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) - instrument :url, key do |payload| + instrument :url, key: key do |payload| verified_token_with_expiration = ActiveStorage.verifier.generate( { key: key, diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb index fd9916634a..c13ce4786d 100644 --- a/activestorage/lib/active_storage/service/gcs_service.rb +++ b/activestorage/lib/active_storage/service/gcs_service.rb @@ -14,7 +14,7 @@ module ActiveStorage end def upload(key, io, checksum: nil) - instrument :upload, key, checksum: checksum do + instrument :upload, key: key, checksum: checksum do begin bucket.create_file(io, key, md5: checksum) rescue Google::Cloud::InvalidArgumentError @@ -25,7 +25,7 @@ module ActiveStorage # FIXME: Download in chunks when given a block. def download(key) - instrument :download, key do + instrument :download, key: key do io = file_for(key).download io.rewind @@ -38,7 +38,7 @@ module ActiveStorage end def delete(key) - instrument :delete, key do + instrument :delete, key: key do begin file_for(key).delete rescue Google::Cloud::NotFoundError @@ -47,8 +47,14 @@ module ActiveStorage end end + def delete_prefixed(prefix) + instrument :delete_prefixed, prefix: prefix do + bucket.files(prefix: prefix).all(&:delete) + end + end + def exist?(key) - instrument :exist, key do |payload| + instrument :exist, key: key do |payload| answer = file_for(key).exists? payload[:exist] = answer answer @@ -56,7 +62,7 @@ module ActiveStorage end def url(key, expires_in:, filename:, content_type:, disposition:) - instrument :url, key do |payload| + instrument :url, key: key do |payload| generated_url = file_for(key).signed_url expires: expires_in, query: { "response-content-disposition" => content_disposition_with(type: disposition, filename: filename), "response-content-type" => content_type @@ -69,7 +75,7 @@ module ActiveStorage end def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) - instrument :url, key do |payload| + instrument :url, key: key do |payload| generated_url = bucket.signed_url key, method: "PUT", expires: expires_in, content_type: content_type, content_md5: checksum diff --git a/activestorage/lib/active_storage/service/mirror_service.rb b/activestorage/lib/active_storage/service/mirror_service.rb index 39e922f7ab..7eca8ce7f4 100644 --- a/activestorage/lib/active_storage/service/mirror_service.rb +++ b/activestorage/lib/active_storage/service/mirror_service.rb @@ -35,6 +35,11 @@ module ActiveStorage perform_across_services :delete, key end + # Delete files at keys starting with the +prefix+ on all services. + def delete_prefixed(prefix) + perform_across_services :delete_prefixed, prefix + end + private def each_service(&block) [ primary, *mirrors ].each(&block) diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb index 6957119780..c95672f338 100644 --- a/activestorage/lib/active_storage/service/s3_service.rb +++ b/activestorage/lib/active_storage/service/s3_service.rb @@ -17,7 +17,7 @@ module ActiveStorage end def upload(key, io, checksum: nil) - instrument :upload, key, checksum: checksum do + instrument :upload, key: key, checksum: checksum do begin object_for(key).put(upload_options.merge(body: io, content_md5: checksum)) rescue Aws::S3::Errors::BadDigest @@ -28,24 +28,30 @@ module ActiveStorage def download(key, &block) if block_given? - instrument :streaming_download, key do + instrument :streaming_download, key: key do stream(key, &block) end else - instrument :download, key do + instrument :download, key: key do object_for(key).get.body.read.force_encoding(Encoding::BINARY) end end end def delete(key) - instrument :delete, key do + instrument :delete, key: key do object_for(key).delete end end + def delete_prefixed(prefix) + instrument :delete_prefixed, prefix: prefix do + bucket.objects(prefix: prefix).batch_delete! + end + end + def exist?(key) - instrument :exist, key do |payload| + instrument :exist, key: key do |payload| answer = object_for(key).exists? payload[:exist] = answer answer @@ -53,7 +59,7 @@ module ActiveStorage end def url(key, expires_in:, filename:, disposition:, content_type:) - instrument :url, key do |payload| + instrument :url, key: key do |payload| generated_url = object_for(key).presigned_url :get, expires_in: expires_in.to_i, response_content_disposition: content_disposition_with(type: disposition, filename: filename), response_content_type: content_type @@ -65,7 +71,7 @@ module ActiveStorage end def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) - instrument :url, key do |payload| + instrument :url, key: key do |payload| generated_url = object_for(key).presigned_url :put, expires_in: expires_in.to_i, content_type: content_type, content_length: content_length, content_md5: checksum diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb index 6e815997ba..f94e65ed77 100644 --- a/activestorage/test/models/blob_test.rb +++ b/activestorage/test/models/blob_test.rb @@ -41,13 +41,21 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase end end - test "purge removes from external service" do + test "purge deletes file from external service" do blob = create_blob blob.purge assert_not ActiveStorage::Blob.service.exist?(blob.key) end + test "purge deletes variants from external service" do + blob = create_file_blob + variant = blob.variant(resize: "100>").processed + + blob.purge + assert_not ActiveStorage::Blob.service.exist?(variant.key) + end + private def expected_url_for(blob, disposition: :inline) query_string = { content_type: blob.content_type, disposition: "#{disposition}; #{blob.filename.parameters}" }.to_param diff --git a/activestorage/test/service/shared_service_tests.rb b/activestorage/test/service/shared_service_tests.rb index ade91ab89a..ce28c4393a 100644 --- a/activestorage/test/service/shared_service_tests.rb +++ b/activestorage/test/service/shared_service_tests.rb @@ -75,5 +75,22 @@ module ActiveStorage::Service::SharedServiceTests @service.delete SecureRandom.base58(24) end end + + test "deleting by prefix" do + begin + @service.upload("a/a/a", StringIO.new(FIXTURE_DATA)) + @service.upload("a/a/b", StringIO.new(FIXTURE_DATA)) + @service.upload("a/b/a", StringIO.new(FIXTURE_DATA)) + + @service.delete_prefixed("a/a/") + assert_not @service.exist?("a/a/a") + assert_not @service.exist?("a/a/b") + assert @service.exist?("a/b/a") + ensure + @service.delete("a/a/a") + @service.delete("a/a/b") + @service.delete("a/b/a") + end + end end end -- cgit v1.2.3 From d0f5dce492696019ddf409892829f89bee5f45ef Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 15:00:05 +0900 Subject: `change_column_default` should be executed after type changing If do not execute a type changing first, filling in default value may be failed. ``` % ARCONN=postgresql be ruby -w -Itest test/cases/migration/compatibility_test.rb -n test_legacy_change_column_with_null_executes_update Using postgresql Run options: -n test_legacy_change_column_with_null_executes_update --seed 20459 E Error: ActiveRecord::Migration::CompatibilityTest#test_legacy_change_column_with_null_executes_update: StandardError: An error has occurred, this and all later migrations canceled: PG::StringDataRightTruncation: ERROR: value too long for type character varying(5) : UPDATE "testings" SET "foo"='foobar' WHERE "foo" IS NULL ``` --- .../connection_adapters/postgresql/schema_statements.rb | 15 ++++++++------- .../lib/active_record/migration/compatibility.rb | 17 ++++++++++------- activerecord/test/cases/migration/compatibility_test.rb | 4 ++-- 3 files changed, 20 insertions(+), 16 deletions(-) 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 2e9e74403b..bf5fbb30e1 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -393,9 +393,9 @@ module ActiveRecord end def change_column(table_name, column_name, type, options = {}) #:nodoc: - quoted_table_name = quote_table_name(table_name) + clear_cache! sqls, procs = change_column_for_alter(table_name, column_name, type, options) - execute "ALTER TABLE #{quoted_table_name} #{sqls.join(", ")}" + execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}" procs.each(&:call) end @@ -646,8 +646,7 @@ module ActiveRecord end end - def change_column_for_alter(table_name, column_name, type, options = {}) - clear_cache! + def change_column_sql(table_name, column_name, type, options = {}) quoted_column_name = quote_column_name(column_name) sql_type = type_to_sql(type, options) sql = "ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}".dup @@ -661,7 +660,11 @@ module ActiveRecord sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})" end - sqls = [sql] + sql + end + + def change_column_for_alter(table_name, column_name, type, options = {}) + sqls = [change_column_sql(table_name, column_name, type, options)] procs = [] sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default) sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null) @@ -673,7 +676,6 @@ module ActiveRecord # Changes the default value of a table column. def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc: - clear_cache! column = column_for(table_name, column_name) return unless column @@ -689,7 +691,6 @@ module ActiveRecord end def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc: - clear_cache! "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL" end diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index da307c1723..bd8c054c28 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -16,13 +16,16 @@ module ActiveRecord V5_2 = Current class V5_1 < V5_2 - if adapter_name == "PostgreSQL" - def change_column(table_name, column_name, type, options = {}) - unless options[:null] || options[:default].nil? - column = connection.send(:column_for, table_name, column_name) - connection.execute("UPDATE #{connection.quote_table_name(table_name)} SET #{connection.quote_column_name(column_name)}=#{connection.quote_default_expression(options[:default], column)} WHERE #{connection.quote_column_name(column_name)} IS NULL") if column - end - connection.change_column(table_name, column_name, type, options) + def change_column(table_name, column_name, type, options = {}) + if adapter_name == "PostgreSQL" + clear_cache! + sql = connection.send(:change_column_sql, table_name, column_name, type, options) + execute "ALTER TABLE #{quote_table_name(table_name)} #{sql}" + change_column_default(table_name, column_name, options[:default]) if options.key?(:default) + change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null) + change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment) + else + super end end end diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index e6ca252369..cc2391f349 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -16,7 +16,7 @@ module ActiveRecord ActiveRecord::Migration.verbose = false connection.create_table :testings do |t| - t.column :foo, :string, limit: 100 + t.column :foo, :string, limit: 5 t.column :bar, :string, limit: 100 end end @@ -134,7 +134,7 @@ module ActiveRecord def test_legacy_change_column_with_null_executes_update migration = Class.new(ActiveRecord::Migration[5.1]) { def migrate(x) - change_column :testings, :foo, :string, null: false, default: "foobar" + change_column :testings, :foo, :string, limit: 10, null: false, default: "foobar" end }.new -- cgit v1.2.3 From 70fa9e9ab7fd89589664ecd7ee367448ef45f9d8 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 15:28:10 +0900 Subject: Emulate JSON types for SQLite3 adapter (#29664) Actually SQLite3 doesn't have JSON storage class (so it is stored as a TEXT like Date and Time). But emulating JSON types is convinient for making database agnostic migrations. --- .../connection_adapters/abstract/schema_definitions.rb | 1 + .../active_record/connection_adapters/mysql/schema_definitions.rb | 4 ---- .../connection_adapters/postgresql/schema_definitions.rb | 4 ---- .../lib/active_record/connection_adapters/sqlite3_adapter.rb | 7 ++++++- activerecord/test/cases/adapters/sqlite3/json_test.rb | 4 ++-- activerecord/test/cases/json_shared_test_cases.rb | 2 -- 6 files changed, 9 insertions(+), 13 deletions(-) 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 86406d95ad..0594b4b485 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -220,6 +220,7 @@ module ActiveRecord :decimal, :float, :integer, + :json, :string, :text, :time, diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb index da25e4863c..2ed4ad16ae 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb @@ -32,10 +32,6 @@ module ActiveRecord args.each { |name| column(name, :longtext, options) } end - def json(*args, **options) - args.each { |name| column(name, :json, options) } - end - def unsigned_integer(*args, **options) args.each { |name| column(name, :unsigned_integer, options) } end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb index a3bb66bf3e..6047217fcd 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb @@ -95,10 +95,6 @@ module ActiveRecord args.each { |name| column(name, :int8range, options) } end - def json(*args, **options) - args.each { |name| column(name, :json, options) } - end - def jsonb(*args, **options) args.each { |name| column(name, :jsonb, options) } end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index daece2bffd..ff63f63bf7 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -70,7 +70,8 @@ module ActiveRecord time: { name: "time" }, date: { name: "date" }, binary: { name: "blob" }, - boolean: { name: "boolean" } + boolean: { name: "boolean" }, + json: { name: "json" }, } ## @@ -134,6 +135,10 @@ module ActiveRecord true end + def supports_json? + true + end + def supports_multi_insert? sqlite_version >= "3.7.11" end diff --git a/activerecord/test/cases/adapters/sqlite3/json_test.rb b/activerecord/test/cases/adapters/sqlite3/json_test.rb index 568a524058..6f247fcd22 100644 --- a/activerecord/test/cases/adapters/sqlite3/json_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/json_test.rb @@ -9,8 +9,8 @@ class SQLite3JSONTest < ActiveRecord::SQLite3TestCase def setup super @connection.create_table("json_data_type") do |t| - t.column "payload", :json, default: {} - t.column "settings", :json + t.json "payload", default: {} + t.json "settings" end end diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb index a71485982c..56ec8c8a82 100644 --- a/activerecord/test/cases/json_shared_test_cases.rb +++ b/activerecord/test/cases/json_shared_test_cases.rb @@ -30,7 +30,6 @@ module JSONSharedTestCases end def test_change_table_supports_json - skip unless @connection.supports_json? @connection.change_table("json_data_type") do |t| t.public_send column_type, "users" end @@ -41,7 +40,6 @@ module JSONSharedTestCases end def test_schema_dumping - skip unless @connection.supports_json? output = dump_table_schema("json_data_type") assert_match(/t\.#{column_type}\s+"settings"/, output) end -- cgit v1.2.3 From 9b823c02b62a91216ef1ad0c9d4fca095377afb6 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 15:45:40 +0900 Subject: SQLite3 valid integer value should be 8 bytes (64-bit signed integer) (#28379) This is a regression since Rails 4.2. SQLite3 integer is stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value. Assuming default valid value as 4 bytes caused that actual valid value in INTEGER storage class cannot be stored and existing value cannot be found. https://www.sqlite.org/datatype3.html We should allow valid value in INTEGER storage class in SQLite3 to fix the regression. Fixes #22594. --- .../active_record/connection_adapters/sqlite3_adapter.rb | 15 +++++++++++++++ activerecord/test/cases/base_test.rb | 8 +++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index ff63f63bf7..574a7b4572 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -374,6 +374,10 @@ module ActiveRecord end private + def initialize_type_map(m = type_map) + super + register_class_with_limit m, %r(int)i, SQLite3Integer + end def table_structure(table_name) structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA") @@ -529,6 +533,17 @@ module ActiveRecord def configure_connection execute("PRAGMA foreign_keys = ON", "SCHEMA") end + + class SQLite3Integer < Type::Integer # :nodoc: + private + def _limit + # INTEGER storage class can be stored 8 bytes value. + # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes + limit || 8 + end + end + + ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3) end ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter) end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index d79afa2ee9..3497f6aae4 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -891,11 +891,9 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 2147483648, company.rating end - unless current_adapter?(:SQLite3Adapter) - def test_bignum_pk - company = Company.create!(id: 2147483648, name: "foo") - assert_equal company, Company.find(company.id) - end + def test_bignum_pk + company = Company.create!(id: 2147483648, name: "foo") + assert_equal company, Company.find(company.id) end # TODO: extend defaults tests to other databases! -- cgit v1.2.3 From 7609ca08ce5000689838eeb04ed37084bf364f78 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 3 Dec 2017 18:06:29 +0100 Subject: Fix instrumention name: delete_prefixed like the others. --- activestorage/lib/active_storage/service/azure_storage_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb index 98de0836de..19b09991b3 100644 --- a/activestorage/lib/active_storage/service/azure_storage_service.rb +++ b/activestorage/lib/active_storage/service/azure_storage_service.rb @@ -52,7 +52,7 @@ module ActiveStorage end def delete_prefixed(prefix) - instrument :delete_all, prefix: prefix do + instrument :delete_prefixed, prefix: prefix do marker = nil loop do -- cgit v1.2.3 From c8f014ff1f68bca8deb22f57dd8c7fde9deb7030 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 3 Dec 2017 19:12:06 +0100 Subject: Embrace the instantiation in loving parens <3 --- actionpack/test/dispatch/session/cookie_store_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 7f70961465..e34426a471 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -13,8 +13,9 @@ class CookieStoreTest < ActionDispatch::IntegrationTest Generator = ActiveSupport::KeyGenerator.new(SessionSecret, iterations: 1000) Rotations = ActiveSupport::Messages::RotationConfiguration.new - Encryptor = ActiveSupport::MessageEncryptor.new \ + Encryptor = ActiveSupport::MessageEncryptor.new( Generator.generate_key(SessionSalt, 32), cipher: "aes-256-gcm", serializer: Marshal + ) class TestController < ActionController::Base def no_session_access -- cgit v1.2.3 From 20bb26a428b84ae4f7b09bec284152325b0334f6 Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Mon, 4 Dec 2017 01:18:59 +0200 Subject: Update "Active Record Query Interface" guide [ci skip] - Add missing `LIMIT 1` for some queries - Make some examples of query more readable --- guides/source/active_record_querying.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 3786343fc3..4e28e31a53 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -801,7 +801,7 @@ The SQL that would be executed: SELECT * FROM articles WHERE id > 10 ORDER BY id DESC # Original query without `only` -SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20 +SELECT * FROM articles WHERE id > 10 ORDER BY id DESC LIMIT 20 ``` @@ -820,14 +820,14 @@ Article.find(10).comments.reorder('name') The SQL that would be executed: ```sql -SELECT * FROM articles WHERE id = 10 +SELECT * FROM articles WHERE id = 10 LIMIT 1 SELECT * FROM comments WHERE article_id = 10 ORDER BY name ``` In the case where the `reorder` clause is not used, the SQL executed would be: ```sql -SELECT * FROM articles WHERE id = 10 +SELECT * FROM articles WHERE id = 10 LIMIT 1 SELECT * FROM comments WHERE article_id = 10 ORDER BY posted_at DESC ``` @@ -1091,7 +1091,7 @@ This produces: ```sql SELECT articles.* FROM articles - INNER JOIN categories ON articles.category_id = categories.id + INNER JOIN categories ON categories.id = articles.category_id INNER JOIN comments ON comments.article_id = articles.id ``` @@ -1871,14 +1871,14 @@ All calculation methods work directly on a model: ```ruby Client.count -# SELECT count(*) AS count_all FROM clients +# SELECT COUNT(*) FROM clients ``` Or on a relation: ```ruby Client.where(first_name: 'Ryan').count -# SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan') +# SELECT COUNT(*) FROM clients WHERE (first_name = 'Ryan') ``` You can also use various finder methods on a relation for performing complex calculations: @@ -1890,9 +1890,9 @@ Client.includes("orders").where(first_name: 'Ryan', orders: { status: 'received' Which will execute: ```sql -SELECT count(DISTINCT clients.id) AS count_all FROM clients - LEFT OUTER JOIN orders ON orders.client_id = clients.id WHERE - (clients.first_name = 'Ryan' AND orders.status = 'received') +SELECT COUNT(DISTINCT clients.id) FROM clients + LEFT OUTER JOIN orders ON orders.client_id = clients.id + WHERE (clients.first_name = 'Ryan' AND orders.status = 'received') ``` ### Count -- cgit v1.2.3 From 07788c7ad8bad797ec97cba038e37e007f343afa Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 4 Dec 2017 08:18:31 +0900 Subject: `current_version` should catch `NoDatabaseError` from `get_all_versions` `get_all_versions` doesn't use passed `connection`. So it should be caught `NoDatabaseError` from `SchemaMigration.table_exists?`. --- activerecord/lib/active_record/migration.rb | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 4d4b0dc67a..5c10d4ff24 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1045,7 +1045,7 @@ module ActiveRecord new(:up, migrations(migrations_paths), nil) end - def get_all_versions(connection = Base.connection) + def get_all_versions if SchemaMigration.table_exists? SchemaMigration.all_versions.map(&:to_i) else @@ -1054,19 +1054,12 @@ module ActiveRecord end def current_version(connection = nil) - if connection.nil? - begin - connection = Base.connection - rescue ActiveRecord::NoDatabaseError - return nil - end - end - - get_all_versions(connection).max || 0 + get_all_versions.max || 0 + rescue ActiveRecord::NoDatabaseError end - def needs_migration?(connection = Base.connection) - (migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0 + def needs_migration?(connection = nil) + (migrations(migrations_paths).collect(&:version) - get_all_versions).size > 0 end def any_migrations? -- cgit v1.2.3 From 915f0e682cecf80080914d0390ce22eb06f41470 Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Wed, 29 Nov 2017 18:44:46 +0900 Subject: Add tests for the `--webpack` option We probably don't have any tests for the `--webpack` option. related: #27288 --- railties/test/generators/app_generator_test.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 774fd0f315..c9c85078d4 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -743,6 +743,20 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_webpack_option + command_check = -> command, *_ do + @called ||= 0 + @called += 1 if command == "webpacker:install" + assert_equal 1, @called, "webpacker:install expected to be called once, but was called #{@called} times." + end + + generator([destination_root], webpack: true).stub(:rails_command, command_check) do + quietly { generator.invoke_all } + end + + assert_gem "webpacker" + end + def test_generator_if_skip_turbolinks_is_given run_generator [destination_root, "--skip-turbolinks"] -- cgit v1.2.3 From 649f19cab1d4dd805c915912ede29c86655084cd Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Tue, 5 Dec 2017 15:25:12 +0900 Subject: Fix example code in ActiveJob::Core [ci skip] 1) It seems that it raise error on example code in `ActiveJob::Core`. Before: ```ruby class DeliverWebhookJob < ActiveJob::Base def serialize super.merge('attempt_number' => (@attempt_number || 0) + 1) end def deserialize(job_data) super @attempt_number = job_data['attempt_number'] end rescue_from(Timeout::Error) do |exception| raise exception if @attempt_number > 5 retry_job(wait: 10) end def perform raise Timeout::Error end end ``` Then it run `DeliverWebhookJob.perform_now` in `rails console`. And raise error: NoMethodError: undefined method `>' for nil:NilClass from /app/jobs/deliver_webhook_job.rb:12:in `block in ' So I thought it's necessary to fix it. After: ```ruby class DeliverWebhookJob < ActiveJob::Base attr_writer :attempt_number def attempt_number @attempt_number ||= 0 end def serialize super.merge('attempt_number' => attempt_number + 1) end def deserialize(job_data) super self.attempt_number = job_data['attempt_number'] end rescue_from(Timeout::Error) do |exception| raise exception if attempt_number > 5 retry_job(wait: 10) end def perform raise Timeout::Error end end ``` Then it run `DeliverWebhookJob.perform_now` in `rails console`. And it does'nt raise error NoMethodError. 2) Use `Timeout::Error` instead of `TimeoutError` (`TimeoutError` is deprecated). --- activejob/lib/active_job/core.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb index c4e12fc518..879746fc01 100644 --- a/activejob/lib/active_job/core.rb +++ b/activejob/lib/active_job/core.rb @@ -97,17 +97,23 @@ module ActiveJob # ==== Examples # # class DeliverWebhookJob < ActiveJob::Base + # attr_writer :attempt_number + # + # def attempt_number + # @attempt_number ||= 0 + # end + # # def serialize - # super.merge('attempt_number' => (@attempt_number || 0) + 1) + # super.merge('attempt_number' => attempt_number + 1) # end # # def deserialize(job_data) # super - # @attempt_number = job_data['attempt_number'] + # self.attempt_number = job_data['attempt_number'] # end # - # rescue_from(TimeoutError) do |exception| - # raise exception if @attempt_number > 5 + # rescue_from(Timeout::Error) do |exception| + # raise exception if attempt_number > 5 # retry_job(wait: 10) # end # end -- cgit v1.2.3 From 3c442b6df91e291ebbf17f37444414bf5f10fbe6 Mon Sep 17 00:00:00 2001 From: Simon Dawson Date: Tue, 5 Dec 2017 07:13:48 +0000 Subject: Fix CSP copy boolean directives (#31326) Use Object#deep_dup to safely duplicate policy values --- actionpack/lib/action_dispatch/http/content_security_policy.rb | 6 +----- actionpack/test/dispatch/content_security_policy_test.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb index d10d4faf3d..c888a27720 100644 --- a/actionpack/lib/action_dispatch/http/content_security_policy.rb +++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb @@ -110,7 +110,7 @@ module ActionDispatch #:nodoc: end def initialize_copy(other) - @directives = copy_directives(other.directives) + @directives = other.directives.deep_dup end DIRECTIVES.each do |name, directive| @@ -174,10 +174,6 @@ module ActionDispatch #:nodoc: end private - def copy_directives(directives) - directives.transform_values { |sources| sources.map(&:dup) } - end - def apply_mappings(sources) sources.map do |source| case source diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb index 8a1ac066e8..7c4a65a633 100644 --- a/actionpack/test/dispatch/content_security_policy_test.rb +++ b/actionpack/test/dispatch/content_security_policy_test.rb @@ -14,6 +14,15 @@ class ContentSecurityPolicyTest < ActiveSupport::TestCase assert_equal "script-src 'self';", @policy.build end + def test_dup + @policy.img_src :self + @policy.block_all_mixed_content + @policy.upgrade_insecure_requests + @policy.sandbox + copied = @policy.dup + assert_equal copied.build, @policy.build + end + def test_mappings @policy.script_src :data assert_equal "script-src data:;", @policy.build -- cgit v1.2.3 From 7efb4d23c1022108319add6218ae9d9284936ac5 Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Tue, 5 Dec 2017 18:16:41 +0900 Subject: Add missing require Follow up of 3c442b6df91e291ebbf17f37444414bf5f10fbe6 Without this require, it will fail when run CSP test alone. Ref: https://travis-ci.org/rails/rails/jobs/311715758#L2976 --- actionpack/lib/action_dispatch/http/content_security_policy.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb index c888a27720..4883e23d24 100644 --- a/actionpack/lib/action_dispatch/http/content_security_policy.rb +++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "active_support/core_ext/object/deep_dup" + module ActionDispatch #:nodoc: class ContentSecurityPolicy class Middleware -- cgit v1.2.3 From b9fb74514b752df3b707fa420e09dca459a3844f Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Tue, 5 Dec 2017 18:41:44 +0900 Subject: Modify `test_webpack_option` --- railties/test/generators/app_generator_test.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index decea77d48..68b31148d6 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -746,11 +746,13 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_webpack_option command_check = -> command, *_ do @called ||= 0 - @called += 1 if command == "webpacker:install" - assert_equal 1, @called, "webpacker:install expected to be called once, but was called #{@called} times." + if command == "webpacker:install" + @called += 1 + assert_equal 1, @called, "webpacker:install expected to be called once, but was called #{@called} times." + end end - generator([destination_root], webpack: true).stub(:rails_command, command_check) do + generator([destination_root], webpack: "webpack").stub(:rails_command, command_check) do quietly { generator.invoke_all } end -- cgit v1.2.3 From 6a11b0c1549e88a7ca32a73764d8b6634dc326e2 Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Tue, 5 Dec 2017 18:43:15 +0900 Subject: Add more tests for the `--webpack` option --- railties/test/generators/app_generator_test.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 68b31148d6..96803db838 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -759,6 +759,25 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_gem "webpacker" end + def test_webpack_option_with_js_framework + command_check = -> command, *_ do + case command + when "webpacker:install" + @webpacker ||= 0 + @webpacker += 1 + assert_equal 1, @webpacker, "webpacker:install expected to be called once, but was called #{@webpacker} times." + when "webpacker:install:react" + @react ||= 0 + @react += 1 + assert_equal 1, @react, "webpacker:install:react expected to be called once, but was called #{@react} times." + end + end + + generator([destination_root], webpack: "react").stub(:rails_command, command_check) do + quietly { generator.invoke_all } + end + end + def test_generator_if_skip_turbolinks_is_given run_generator [destination_root, "--skip-turbolinks"] -- cgit v1.2.3 From 6ec0ed67d9afcc666ad0424b10e9903f63e60714 Mon Sep 17 00:00:00 2001 From: Joe Francis Date: Tue, 5 Dec 2017 08:23:30 -0600 Subject: only install ffmpeg and mupdf on activestorage builds These are needed when GEM=ast, thanks @georgeclaghorn --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 290e0b5f2b..fd3b3f9002 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,10 +20,6 @@ addons: sources: - sourceline: "ppa:mc3man/trusty-media" - sourceline: "ppa:ubuntuhandbook1/apps" - packages: - - ffmpeg - - mupdf - - mupdf-tools bundler_args: --without test --jobs 3 --retry 3 before_install: @@ -42,6 +38,7 @@ before_script: # Decodes to e.g. `export VARIABLE=VALUE` - $(base64 --decode <<< "ZXhwb3J0IFNBVUNFX0FDQ0VTU19LRVk9YTAzNTM0M2YtZTkyMi00MGIzLWFhM2MtMDZiM2VhNjM1YzQ4") - $(base64 --decode <<< "ZXhwb3J0IFNBVUNFX1VTRVJOQU1FPXJ1YnlvbnJhaWxz") + - if [[ $GEM = *ast* ]] ; then sudo apt-get update && sudo apt-get -y install ffmpeg mupdf mupdf-tools ; fi script: 'ci/travis.rb' -- cgit v1.2.3 From 29d081c47a43cc5d979836541cb73a816c95981d Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Tue, 5 Dec 2017 19:16:11 +0000 Subject: Execute `JsonAttributeTest` only if `supports_json?` returns `true` Oracle enhanced adapter does not fully support JSON datatype then `supports_json?` returns `false`. I wanted to skip known failures and errors when tested with Oracle enhanced adapter. --- activerecord/test/cases/json_attribute_test.rb | 46 ++++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/activerecord/test/cases/json_attribute_test.rb b/activerecord/test/cases/json_attribute_test.rb index 63f3c77fc3..a6fd4f34dc 100644 --- a/activerecord/test/cases/json_attribute_test.rb +++ b/activerecord/test/cases/json_attribute_test.rb @@ -3,33 +3,35 @@ require "cases/helper" require "cases/json_shared_test_cases" -class JsonAttributeTest < ActiveRecord::TestCase - include JSONSharedTestCases - self.use_transactional_tests = false +if ActiveRecord::Base.connection.supports_json? + class JsonAttributeTest < ActiveRecord::TestCase + include JSONSharedTestCases + self.use_transactional_tests = false - class JsonDataTypeOnText < ActiveRecord::Base - self.table_name = "json_data_type" + class JsonDataTypeOnText < ActiveRecord::Base + self.table_name = "json_data_type" - attribute :payload, :json - attribute :settings, :json + attribute :payload, :json + attribute :settings, :json - store_accessor :settings, :resolution - end - - def setup - super - @connection.create_table("json_data_type") do |t| - t.text "payload" - t.text "settings" + store_accessor :settings, :resolution end - end - private - def column_type - :text + def setup + super + @connection.create_table("json_data_type") do |t| + t.text "payload" + t.text "settings" + end end - def klass - JsonDataTypeOnText - end + private + def column_type + :text + end + + def klass + JsonDataTypeOnText + end + end end -- cgit v1.2.3 From d2321aacc7451f6b4f154d894adcb76820abba3c Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 6 Dec 2017 08:30:27 +0900 Subject: Revert "Merge pull request #31341 from yahonda/skip_json_attribute_test" This reverts commit 23226d04f921b79f0077ba38c5a5a923b6d43f89, reversing changes made to 7544cf7603959f25100b21f70b5e70354bed7e45. --- activerecord/test/cases/json_attribute_test.rb | 46 ++++++++++++-------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/activerecord/test/cases/json_attribute_test.rb b/activerecord/test/cases/json_attribute_test.rb index a6fd4f34dc..63f3c77fc3 100644 --- a/activerecord/test/cases/json_attribute_test.rb +++ b/activerecord/test/cases/json_attribute_test.rb @@ -3,35 +3,33 @@ require "cases/helper" require "cases/json_shared_test_cases" -if ActiveRecord::Base.connection.supports_json? - class JsonAttributeTest < ActiveRecord::TestCase - include JSONSharedTestCases - self.use_transactional_tests = false +class JsonAttributeTest < ActiveRecord::TestCase + include JSONSharedTestCases + self.use_transactional_tests = false - class JsonDataTypeOnText < ActiveRecord::Base - self.table_name = "json_data_type" + class JsonDataTypeOnText < ActiveRecord::Base + self.table_name = "json_data_type" - attribute :payload, :json - attribute :settings, :json + attribute :payload, :json + attribute :settings, :json - store_accessor :settings, :resolution - end + store_accessor :settings, :resolution + end - def setup - super - @connection.create_table("json_data_type") do |t| - t.text "payload" - t.text "settings" - end + def setup + super + @connection.create_table("json_data_type") do |t| + t.text "payload" + t.text "settings" end + end - private - def column_type - :text - end + private + def column_type + :text + end - def klass - JsonDataTypeOnText - end - end + def klass + JsonDataTypeOnText + end end -- cgit v1.2.3 From d721344280346f67c7d2dbffa9eaf9341e73673d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 6 Dec 2017 08:33:16 +0900 Subject: Use `:string` instead of `:text` for `JsonAttributeTest` Since CLOB data type has many limitations in Oracle SELECT WHERE clause. --- activerecord/test/cases/json_attribute_test.rb | 6 +++--- activerecord/test/cases/json_shared_test_cases.rb | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/activerecord/test/cases/json_attribute_test.rb b/activerecord/test/cases/json_attribute_test.rb index 63f3c77fc3..afc39d0420 100644 --- a/activerecord/test/cases/json_attribute_test.rb +++ b/activerecord/test/cases/json_attribute_test.rb @@ -19,14 +19,14 @@ class JsonAttributeTest < ActiveRecord::TestCase def setup super @connection.create_table("json_data_type") do |t| - t.text "payload" - t.text "settings" + t.string "payload" + t.string "settings" end end private def column_type - :text + :string end def klass diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb index 56ec8c8a82..012aaffde1 100644 --- a/activerecord/test/cases/json_shared_test_cases.rb +++ b/activerecord/test/cases/json_shared_test_cases.rb @@ -23,7 +23,7 @@ module JSONSharedTestCases def test_column column = klass.columns_hash["payload"] assert_equal column_type, column.type - assert_equal column_type.to_s, column.sql_type + assert_type_match column_type, column.sql_type type = klass.type_for_attribute("payload") assert_not type.binary? @@ -36,7 +36,7 @@ module JSONSharedTestCases klass.reset_column_information column = klass.columns_hash["users"] assert_equal column_type, column.type - assert_equal column_type.to_s, column.sql_type + assert_type_match column_type, column.sql_type end def test_schema_dumping @@ -253,4 +253,9 @@ module JSONSharedTestCases def klass JsonDataType end + + def assert_type_match(type, sql_type) + native_type = ActiveRecord::Base.connection.native_database_types[type][:name] + assert_match %r(\A#{native_type}\b), sql_type + end end -- cgit v1.2.3 From 5f7fd1584f131d0afad558eaba6017e08df65892 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Wed, 6 Dec 2017 09:09:32 +0900 Subject: Add `assert_in_epsilon` to Testing guide [ci skip] I found `assert_in_epsilon` is not be in "2.4 Available Assertions". So I Added them. MiniTest::Assertions#assert_in_epsilon: https://github.com/seattlerb/minitest/blob/master/lib/minitest/assertions.rb#L204-L210 --- guides/source/testing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/guides/source/testing.md b/guides/source/testing.md index e0a2d281d9..9692f50b6e 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -319,6 +319,8 @@ specify to make your test failure messages clearer. | `assert_not_includes( collection, obj, [msg] )` | Ensures that `obj` is not in `collection`.| | `assert_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.| | `assert_not_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.| +| `assert_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` have a relative error less than `epsilon`.| +| `assert_not_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` don't have a relative error less than `epsilon`.| | `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.| | `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.| | `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.| -- cgit v1.2.3 From d7a121a0d624dd8180556521f0611d4c8e2c648f Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Tue, 24 Jan 2017 10:55:43 +0000 Subject: Yield array from AC::Parameters#each for block with one arg Matches Hash#each behaviour as used in Rails 4. --- .../lib/action_controller/metal/strong_parameters.rb | 2 +- actionpack/test/controller/parameters/accessors_test.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index ef7c4c4c16..a56ac749f8 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -335,7 +335,7 @@ module ActionController # the same way as Hash#each_pair. def each_pair(&block) @parameters.each_pair do |key, value| - yield key, convert_hashes_to_parameters(key, value) + yield [key, convert_hashes_to_parameters(key, value)] end end alias_method :each, :each_pair diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb index 43cabae7d2..154430d4b0 100644 --- a/actionpack/test/controller/parameters/accessors_test.rb +++ b/actionpack/test/controller/parameters/accessors_test.rb @@ -51,6 +51,14 @@ class ParametersAccessorsTest < ActiveSupport::TestCase @params.each { |key, value| assert_not(value.permitted?) if key == "person" } end + test "each returns key,value array for block with arity 1" do + @params.each do |arg| + assert_kind_of Array, arg + assert_equal "person", arg[0] + assert_kind_of ActionController::Parameters, arg[1] + end + end + test "each_pair carries permitted status" do @params.permit! @params.each_pair { |key, value| assert(value.permitted?) if key == "person" } @@ -60,6 +68,14 @@ class ParametersAccessorsTest < ActiveSupport::TestCase @params.each_pair { |key, value| assert_not(value.permitted?) if key == "person" } end + test "each_pair returns key,value array for block with arity 1" do + @params.each_pair do |arg| + assert_kind_of Array, arg + assert_equal "person", arg[0] + assert_kind_of ActionController::Parameters, arg[1] + end + end + test "empty? returns true when params contains no key/value pairs" do params = ActionController::Parameters.new assert params.empty? -- cgit v1.2.3 From b34838cbe29434212eb81ca1e31bb0ab0765e32a Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 6 Dec 2017 13:09:51 +0000 Subject: Address `ActiveRecord::NotNullViolation: OCIError: ORA-01400` for Oracle database which requires primary key value mentioned in insert statement explicitly. --- activerecord/test/cases/json_shared_test_cases.rb | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb index 012aaffde1..b0c0f2c283 100644 --- a/activerecord/test/cases/json_shared_test_cases.rb +++ b/activerecord/test/cases/json_shared_test_cases.rb @@ -66,26 +66,26 @@ module JSONSharedTestCases end def test_rewrite - @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k":"v"}')|) + @connection.execute(insert_statement_per_database('{"k":"v"}')) x = klass.first x.payload = { '"a\'' => "b" } assert x.save! end def test_select - @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k":"v"}')|) + @connection.execute(insert_statement_per_database('{"k":"v"}')) x = klass.first assert_equal({ "k" => "v" }, x.payload) end def test_select_multikey - @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|) + @connection.execute(insert_statement_per_database('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')) x = klass.first assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1, 2, 3] }, x.payload) end def test_null_json - @connection.execute("insert into json_data_type (payload) VALUES(null)") + @connection.execute(insert_statement_per_database("null")) x = klass.first assert_nil(x.payload) end @@ -107,13 +107,13 @@ module JSONSharedTestCases end def test_select_array_json_value - @connection.execute(%q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|) + @connection.execute(insert_statement_per_database('["v0",{"k1":"v1"}]')) x = klass.first assert_equal(["v0", { "k1" => "v1" }], x.payload) end def test_rewrite_array_json_value - @connection.execute(%q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|) + @connection.execute(insert_statement_per_database('["v0",{"k1":"v1"}]')) x = klass.first x.payload = ["v1", { "k2" => "v2" }, "v3"] assert x.save! @@ -258,4 +258,12 @@ module JSONSharedTestCases native_type = ActiveRecord::Base.connection.native_database_types[type][:name] assert_match %r(\A#{native_type}\b), sql_type end + + def insert_statement_per_database(values) + if current_adapter?(:OracleAdapter) + "insert into json_data_type (id, payload) VALUES (json_data_type_seq.nextval, '#{values}')" + else + "insert into json_data_type (payload) VALUES ('#{values}')" + end + end end -- cgit v1.2.3 From 5ac4f4d2563e7f9c5ffaecce4be4b9e2c5b0c081 Mon Sep 17 00:00:00 2001 From: Ashley Ellis Pierce Date: Mon, 4 Dec 2017 16:44:33 -0500 Subject: Fix sqlite migrations with custom primary keys Previously, if a record was created with a custom primary key, that table could not be migrated using sqlite. While attempting to copy the table, the type of the primary key was ignored. Once that was corrected, copying the indexes would fail because custom primary keys are autoindexed by sqlite by default. To correct that, this skips copying the index if the index name begins with "sqlite_". This is a reserved word that indicates that the index is an internal schema object. SQLite prohibits applications from creating objects whose names begin with "sqlite_", so this string should be safe to use as a check. ref https://www.sqlite.org/fileformat2.html#intschema --- .../connection_adapters/sqlite3_adapter.rb | 6 +++++- .../cases/adapters/sqlite3/sqlite3_adapter_test.rb | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index daece2bffd..3197b522b3 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -395,10 +395,11 @@ module ActiveRecord def copy_table(from, to, options = {}) from_primary_key = primary_key(from) + from_primary_key_column = columns(from).select { |column| column.name == from_primary_key }.first options[:id] = false create_table(to, options) do |definition| @definition = definition - @definition.primary_key(from_primary_key) if from_primary_key.present? + @definition.primary_key(from_primary_key, from_primary_key_column.type) if from_primary_key.present? columns(from).each do |column| column_name = options[:rename] ? (options[:rename][column.name] || @@ -422,6 +423,9 @@ module ActiveRecord def copy_table_indexes(from, to, rename = {}) indexes(from).each do |index| name = index.name + # indexes sqlite creates for internal use start with `sqlite_` and + # don't need to be copied + next if name.starts_with?("sqlite_") if to == "a#{from}" name = "t#{name}" elsif from == "a#{to}" diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 1f057fe5c6..14f4997d5b 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -360,6 +360,24 @@ module ActiveRecord end end + class Barcode < ActiveRecord::Base + end + + def test_existing_records_have_custom_primary_key + connection = Barcode.connection + connection.create_table(:barcodes, primary_key: "code", id: :string, limit: 42, force: true) do |t| + t.text :other_attr + end + code = "214fe0c2-dd47-46df-b53b-66090b3c1d40" + Barcode.create! code: code, other_attr: "xxx" + + connection.change_table "barcodes" do |t| + connection.remove_column("barcodes", "other_attr") + end + + assert_equal code, Barcode.first.id + end + def test_supports_extensions assert_not @conn.supports_extensions?, "does not support extensions" end -- cgit v1.2.3 From faf169eedd10326233eaea55ed76089ede608336 Mon Sep 17 00:00:00 2001 From: Philip Tolton Date: Wed, 6 Dec 2017 16:01:00 -0500 Subject: Correct routing test spelling mistake. --- actionpack/test/controller/routing_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index f09051b306..71b01c36a7 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -213,7 +213,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal expected, ActiveSupport::JSON.decode(get(u)) end - def test_regexp_precidence + def test_regexp_precedence rs.draw do get "/whois/:domain", constraints: { domain: /\w+\.[\w\.]+/ }, -- cgit v1.2.3 From 46a4ac8a3656fba62d9fbeee79cf8f7306d6a8aa Mon Sep 17 00:00:00 2001 From: maciej-ka Date: Thu, 7 Dec 2017 01:43:43 +0100 Subject: docs: add example for a nil name in link_to --- actionview/lib/action_view/helpers/url_helper.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 02335c72ec..889562c478 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -139,6 +139,11 @@ module ActionView # link_to "Profiles", controller: "profiles" # # =>
Profiles # + # When name is +nil+ the href is presented instead + # + # link_to nil, "http://example.com" + # # => http://www.example.com + # # You can use a block as well if your link target is hard to fit into the name parameter. ERB example: # # <%= link_to(@profile) do %> -- cgit v1.2.3 From da225c0db640aec1df0920e86c1eb4ca35d82073 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 6 Dec 2017 17:33:16 -0800 Subject: Fix Rails environment when running tests with Ruby I frequently run tests with `ruby`, not with a runner like `rake` or `rails`. When running the test with just `ruby` the `RAILS_ENV` environment variable did not get set to "test", and this would cause the tests to fail (and even mutate the development database!) This commit adds integration tests for running tests with just `ruby` and ensures the environment gets defaulted to "test". I also added a test to ensure that passing an environment to `-e` actually works (and fixed that case too). An interesting / annoying thing is that Minitest picks up it's plugins by asking RubyGems for a list of files: https://github.com/seattlerb/minitest/blob/ca6a71ca901016db09a5ad466b4adea4b52a504a/lib/minitest.rb#L92-L100 This means that RubyGems needs to somehow know about the file before it can return it to Minitest. Since we are not packaging Rails as a Gem before running the integration tests on it (duh, why would you do that?), RubyGems doesn't know about the file, so it can't tell Minitest, so Minitest doesn't automatically require it. This means I had to manually require and insert the plugin in our integration test. I've left comments about that in the test as well. Ugh. --- railties/lib/rails/test_unit/runner.rb | 5 ++- railties/test/application/test_runner_test.rb | 56 +++++++++++++++++++++++++++ railties/test/isolation/abstract_unit.rb | 6 ++- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb index 5c2f6451e2..45e8472f6a 100644 --- a/railties/lib/rails/test_unit/runner.rb +++ b/railties/lib/rails/test_unit/runner.rb @@ -12,8 +12,11 @@ module Rails class << self def attach_before_load_options(opts) + ENV["RAILS_ENV"] = "test" opts.on("--warnings", "-w", "Run with Ruby warnings enabled") {} - opts.on("--environment", "-e", "Run tests in the ENV environment") {} + opts.on("-e", "--environment ENV", "Run tests in the ENV environment") { |e| + ENV["RAILS_ENV"] = e + } end def parse_options(argv) diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index e92a0466dd..27983c3b55 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -569,6 +569,26 @@ module ApplicationTests assert_match "AccountTest#test_truth", output, "passing TEST= should run selected test" end + def test_running_with_ruby_gets_test_env_by_default + file = create_test_for_env("test") + results = Dir.chdir(app_path) { + `ruby -Ilib:test #{file}`.each_line.map { |line| JSON.parse line } + } + assert_equal 1, results.length + failures = results.first["failures"] + flunk(failures.first) unless failures.empty? + end + + def test_running_with_ruby_can_set_env_via_cmdline + file = create_test_for_env("development") + results = Dir.chdir(app_path) { + `ruby -Ilib:test #{file} -e development`.each_line.map { |line| JSON.parse line } + } + assert_equal 1, results.length + failures = results.first["failures"] + flunk(failures.first) unless failures.empty? + end + def test_rake_passes_multiple_TESTOPTS_to_minitest create_test_file :models, "account" output = Dir.chdir(app_path) { `bin/rake test TESTOPTS='-v --seed=1234'` } @@ -727,6 +747,42 @@ module ApplicationTests app_file "db/schema.rb", "" end + def create_test_for_env(env) + app_file "test/models/environment_test.rb", <<-RUBY + require 'test_helper' + class JSONReporter < Minitest::AbstractReporter + def record(result) + puts JSON.dump(klass: result.class.name, + name: result.name, + failures: result.failures, + assertions: result.assertions, + time: result.time) + end + end + + def Minitest.plugin_json_reporter_init(opts) + Minitest.reporter.reporters.clear + Minitest.reporter.reporters << JSONReporter.new + end + + Minitest.extensions << "rails" + Minitest.extensions << "json_reporter" + + # Minitest uses RubyGems to find plugins, and since RubyGems + # doesn't know about the Rails installation we're pointing at, + # Minitest won't require the Rails minitest plugin when we run + # these integration tests. So we have to manually require the + # Minitest plugin here. + require 'minitest/rails_plugin' + + class EnvironmentTest < ActiveSupport::TestCase + def test_environment + assert_equal #{env.dump}, ENV["RAILS_ENV"] + end + end + RUBY + end + def create_test_file(path = :unit, name = "test", pass: true) app_file "test/#{path}/#{name}_test.rb", <<-RUBY require 'test_helper' diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 5b1c06d4e5..96c6f21395 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -358,10 +358,12 @@ module TestHelpers end def app_file(path, contents, mode = "w") - FileUtils.mkdir_p File.dirname("#{app_path}/#{path}") - File.open("#{app_path}/#{path}", mode) do |f| + file_name = "#{app_path}/#{path}" + FileUtils.mkdir_p File.dirname(file_name) + File.open(file_name, mode) do |f| f.puts contents end + file_name end def remove_file(path) -- cgit v1.2.3 From 2fa29037678874b25ad257dff8974055e25c9384 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Hirano Date: Thu, 7 Dec 2017 14:46:59 +0900 Subject: [ci skip] Make Todo classes inherit ApplicationRecord Example codes that use `has_many` or `before_create` in `Module::Concerning` look like active record models. So I've made them inherit `ApplicationRecord`. --- activesupport/lib/active_support/core_ext/module/concerning.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/module/concerning.rb b/activesupport/lib/active_support/core_ext/module/concerning.rb index 370a948eea..800bf213cc 100644 --- a/activesupport/lib/active_support/core_ext/module/concerning.rb +++ b/activesupport/lib/active_support/core_ext/module/concerning.rb @@ -22,7 +22,7 @@ class Module # # == Using comments: # - # class Todo + # class Todo < ApplicationRecord # # Other todo implementation # # ... # @@ -42,7 +42,7 @@ class Module # # Noisy syntax. # - # class Todo + # class Todo < ApplicationRecord # # Other todo implementation # # ... # @@ -70,7 +70,7 @@ class Module # increased overhead can be a reasonable tradeoff even if it reduces our # at-a-glance perception of how things work. # - # class Todo + # class Todo < ApplicationRecord # # Other todo implementation # # ... # @@ -82,7 +82,7 @@ class Module # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to # separate bite-sized concerns. # - # class Todo + # class Todo < ApplicationRecord # # Other todo implementation # # ... # @@ -101,7 +101,7 @@ class Module # end # # Todo.ancestors - # # => [Todo, Todo::EventTracking, Object] + # # => [Todo, Todo::EventTracking, ApplicationRecord, Object] # # This small step has some wonderful ripple effects. We can # * grok the behavior of our class in one glance, -- cgit v1.2.3 From 82b974813b28748e5affcff1d8c4ad60ab2971be Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Thu, 7 Dec 2017 20:02:34 +0200 Subject: Add headless firefox driver to System Tests --- actionpack/CHANGELOG.md | 4 ++++ actionpack/lib/action_dispatch/system_test_case.rb | 8 ++++++-- actionpack/lib/action_dispatch/system_testing/driver.rb | 13 ++++++++++++- actionpack/test/abstract_unit.rb | 4 ++++ actionpack/test/dispatch/system_testing/driver_test.rb | 8 ++++++++ .../test/dispatch/system_testing/system_test_case_test.rb | 6 ++++++ guides/source/testing.md | 3 ++- 7 files changed, 42 insertions(+), 4 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index d120d15770..753dd8589a 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add headless firefox support to System Tests. + + *bogdanvlviv* + * Changed the default system test screenshot output from `inline` to `simple`. `inline` works well for iTerm2 but not everyone uses iTerm2. Some terminals like diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb index 7246e01cff..99d0c06751 100644 --- a/actionpack/lib/action_dispatch/system_test_case.rb +++ b/actionpack/lib/action_dispatch/system_test_case.rb @@ -121,11 +121,15 @@ module ActionDispatch # # driven_by :poltergeist # - # driven_by :selenium, using: :firefox + # driven_by :selenium, screen_size: [800, 800] + # + # driven_by :selenium, using: :chrome # # driven_by :selenium, using: :headless_chrome # - # driven_by :selenium, screen_size: [800, 800] + # driven_by :selenium, using: :firefox + # + # driven_by :selenium, using: :headless_firefox def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {}) self.driver = SystemTesting::Driver.new(driver, using: using, screen_size: screen_size, options: options) end diff --git a/actionpack/lib/action_dispatch/system_testing/driver.rb b/actionpack/lib/action_dispatch/system_testing/driver.rb index 2687772b4b..280989a146 100644 --- a/actionpack/lib/action_dispatch/system_testing/driver.rb +++ b/actionpack/lib/action_dispatch/system_testing/driver.rb @@ -37,6 +37,11 @@ module ActionDispatch browser_options.args << "--headless" browser_options.args << "--disable-gpu" + @options.merge(options: browser_options) + elsif @browser == :headless_firefox + browser_options = Selenium::WebDriver::Firefox::Options.new + browser_options.args << "-headless" + @options.merge(options: browser_options) else @options @@ -44,7 +49,13 @@ module ActionDispatch end def browser - @browser == :headless_chrome ? :chrome : @browser + if @browser == :headless_chrome + :chrome + elsif @browser == :headless_firefox + :firefox + else + @browser + end end def register_selenium(app) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 5262e85a28..55ad9c245e 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -453,3 +453,7 @@ end class DrivenBySeleniumWithHeadlessChrome < ActionDispatch::SystemTestCase driven_by :selenium, using: :headless_chrome end + +class DrivenBySeleniumWithHeadlessFirefox < ActionDispatch::SystemTestCase + driven_by :selenium, using: :headless_firefox +end diff --git a/actionpack/test/dispatch/system_testing/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb index 75feae6fe0..fcdaf7fb4c 100644 --- a/actionpack/test/dispatch/system_testing/driver_test.rb +++ b/actionpack/test/dispatch/system_testing/driver_test.rb @@ -25,6 +25,14 @@ class DriverTest < ActiveSupport::TestCase assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) end + test "initializing the driver with a headless firefox" do + driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :headless_firefox, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" }) + assert_equal :selenium, driver.instance_variable_get(:@name) + assert_equal :headless_firefox, driver.instance_variable_get(:@browser) + assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size) + assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) + end + test "initializing the driver with a poltergeist" do driver = ActionDispatch::SystemTesting::Driver.new(:poltergeist, screen_size: [1400, 1400], options: { js_errors: false }) assert_equal :poltergeist, driver.instance_variable_get(:@name) diff --git a/actionpack/test/dispatch/system_testing/system_test_case_test.rb b/actionpack/test/dispatch/system_testing/system_test_case_test.rb index c6a6aef92b..b078a5abc5 100644 --- a/actionpack/test/dispatch/system_testing/system_test_case_test.rb +++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb @@ -28,6 +28,12 @@ class SetDriverToSeleniumHeadlessChromeTest < DrivenBySeleniumWithHeadlessChrome end end +class SetDriverToSeleniumHeadlessFirefoxTest < DrivenBySeleniumWithHeadlessFirefox + test "uses selenium headless firefox" do + assert_equal :selenium, Capybara.current_driver + end +end + class SetHostTest < DrivenByRackTest test "sets default host" do assert_equal "http://127.0.0.1", Capybara.app_host diff --git a/guides/source/testing.md b/guides/source/testing.md index 9692f50b6e..f28c4c224a 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -673,7 +673,8 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase end ``` -If you want to use a headless browser, you could use Headless Chrome by adding `headless_chrome` in the `:using` argument. +If you want to use a headless browser, you could use Headless Chrome or Headless Firefox by adding +`headless_chrome` or `headless_firefox` in the `:using` argument. ```ruby require "test_helper" -- cgit v1.2.3 From 6a8ce7416d6615a13ee5c4b9f6bcd91cc5adef4d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 8 Dec 2017 02:37:02 +0900 Subject: Fix `scope_for_create` to do not lose polymorphic associations This regression was caused at 213796fb due to polymorphic predicates are combined by `Arel::Nodes::And`. But I'd like to keep that combined because it would help inverting polymorphic predicates correctly (e9ba12f7), and we can collect equality nodes regardless of combined by `Arel::Nodes::And` (`a AND (b AND c) AND d` == `a AND b AND c AND d`). This change fixes the regression to collect equality nodes in `Arel::Nodes::And` as well. Fixes #31338. --- .../lib/active_record/relation/where_clause.rb | 18 ++++++++++++++++-- activerecord/test/cases/relation_test.rb | 2 +- activerecord/test/cases/relations_test.rb | 9 +++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/where_clause.rb b/activerecord/lib/active_record/relation/where_clause.rb index 752bb38481..a502713e56 100644 --- a/activerecord/lib/active_record/relation/where_clause.rb +++ b/activerecord/lib/active_record/relation/where_clause.rb @@ -47,7 +47,7 @@ module ActiveRecord end def to_h(table_name = nil) - equalities = predicates.grep(Arel::Nodes::Equality) + equalities = equalities(predicates) if table_name equalities = equalities.select do |node| node.left.relation.name == table_name @@ -90,6 +90,20 @@ module ActiveRecord end private + def equalities(predicates) + equalities = [] + + predicates.each do |node| + case node + when Arel::Nodes::Equality + equalities << node + when Arel::Nodes::And + equalities.concat equalities(node.children) + end + end + + equalities + end def predicates_unreferenced_by(other) predicates.reject do |n| @@ -121,7 +135,7 @@ module ActiveRecord end def except_predicates(columns) - self.predicates.reject do |node| + predicates.reject do |node| case node when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right) diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index a71d8de521..b424ca91de 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -68,7 +68,7 @@ module ActiveRecord relation = Relation.new(Post, Post.arel_table, Post.predicate_builder) left = relation.table[:id].eq(10) right = relation.table[:id].eq(10) - combine = left.and right + combine = left.or(right) relation.where! combine assert_equal({}, relation.where_values_hash) end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 50ad1d5b26..675aafabda 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1189,6 +1189,15 @@ class RelationTest < ActiveRecord::TestCase assert_equal "hen", hen.name end + def test_create_with_polymorphic_association + author = authors(:david) + post = posts(:welcome) + comment = Comment.where(post: post, author: author).create!(body: "hello") + + assert_equal author, comment.author + assert_equal post, comment.post + end + def test_first_or_create parrot = Bird.where(color: "green").first_or_create(name: "parrot") assert_kind_of Bird, parrot -- cgit v1.2.3 From e8286ee272a3e51daebc198519accd1f6895a8d2 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Thu, 7 Dec 2017 15:14:22 -0500 Subject: Fix customizing Content-Type via GCS service URLs --- activestorage/lib/active_storage/service/gcs_service.rb | 8 +++++++- activestorage/test/service/gcs_service_test.rb | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb index c13ce4786d..6f6f4105fe 100644 --- a/activestorage/lib/active_storage/service/gcs_service.rb +++ b/activestorage/lib/active_storage/service/gcs_service.rb @@ -16,7 +16,13 @@ module ActiveStorage def upload(key, io, checksum: nil) instrument :upload, key: key, checksum: checksum do begin - bucket.create_file(io, key, md5: checksum) + # The official GCS client library doesn't allow us to create a file with no Content-Type metadata. + # We need the file we create to have no Content-Type so we can control it via the response-content-type + # param in signed URLs. Workaround: let the GCS client create the file with an inferred + # Content-Type (usually "application/octet-stream") then clear it. + bucket.create_file(io, key, md5: checksum).update do |file| + file.content_type = nil + end rescue Google::Cloud::InvalidArgumentError raise ActiveStorage::IntegrityError end diff --git a/activestorage/test/service/gcs_service_test.rb b/activestorage/test/service/gcs_service_test.rb index 1860149da9..7efcd60fb7 100644 --- a/activestorage/test/service/gcs_service_test.rb +++ b/activestorage/test/service/gcs_service_test.rb @@ -35,6 +35,20 @@ if SERVICE_CONFIGURATIONS[:gcs] assert_match(/storage\.googleapis\.com\/.*response-content-disposition=inline.*test\.txt.*response-content-type=text%2Fplain/, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain")) end + + test "signed URL response headers" do + begin + key = SecureRandom.base58(24) + data = "Something else entirely!" + @service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data)) + + url = @service.url(key, expires_in: 2.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain") + response = Net::HTTP.get_response(URI(url)) + assert_equal "text/plain", response.header["Content-Type"] + ensure + @service.delete key + end + end end else puts "Skipping GCS Service tests because no GCS configuration was supplied" -- cgit v1.2.3 From 131cc6eab64eeae6c7f508dca4176183144cf3a6 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 8 Dec 2017 10:49:25 +0900 Subject: SQLite: Fix `copy_table` with composite primary keys `connection.primary_key` also return composite primary keys, so `from_primary_key_column` may not be found even if `from_primary_key` is presented. ``` % ARCONN=sqlite3 be ruby -w -Itest test/cases/adapters/sqlite3/sqlite3_adapter_test.rb -n test_copy_table_with_composite_primary_keys Using sqlite3 Run options: -n test_copy_table_with_composite_primary_keys --seed 19041 # Running: E Error: ActiveRecord::ConnectionAdapters::SQLite3AdapterTest#test_copy_table_with_composite_primary_keys: NoMethodError: undefined method `type' for nil:NilClass /path/to/rails/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb:411:in `block in copy_table' ``` This change fixes `copy_table` to do not lose composite primary keys. --- .../connection_adapters/sqlite3_adapter.rb | 10 ++++--- .../cases/adapters/sqlite3/sqlite3_adapter_test.rb | 31 ++++++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 65aba20052..c72db15ce3 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -404,22 +404,24 @@ module ActiveRecord def copy_table(from, to, options = {}) from_primary_key = primary_key(from) - from_primary_key_column = columns(from).select { |column| column.name == from_primary_key }.first options[:id] = false create_table(to, options) do |definition| @definition = definition - @definition.primary_key(from_primary_key, from_primary_key_column.type) if from_primary_key.present? + if from_primary_key.is_a?(Array) + @definition.primary_keys from_primary_key + end columns(from).each do |column| column_name = options[:rename] ? (options[:rename][column.name] || options[:rename][column.name.to_sym] || column.name) : column.name - next if column_name == from_primary_key @definition.column(column_name, column.type, limit: column.limit, default: column.default, precision: column.precision, scale: column.scale, - null: column.null, collation: column.collation) + null: column.null, collation: column.collation, + primary_key: column_name == from_primary_key + ) end yield @definition if block_given? end diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 14f4997d5b..1357719422 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -361,21 +361,48 @@ module ActiveRecord end class Barcode < ActiveRecord::Base + self.primary_key = "code" end - def test_existing_records_have_custom_primary_key + def test_copy_table_with_existing_records_have_custom_primary_key connection = Barcode.connection connection.create_table(:barcodes, primary_key: "code", id: :string, limit: 42, force: true) do |t| t.text :other_attr end code = "214fe0c2-dd47-46df-b53b-66090b3c1d40" - Barcode.create! code: code, other_attr: "xxx" + Barcode.create!(code: code, other_attr: "xxx") connection.change_table "barcodes" do |t| connection.remove_column("barcodes", "other_attr") end assert_equal code, Barcode.first.id + ensure + Barcode.reset_column_information + end + + def test_copy_table_with_composite_primary_keys + connection = Barcode.connection + connection.create_table(:barcodes, primary_key: ["region", "code"], force: true) do |t| + t.string :region + t.string :code + t.text :other_attr + end + region = "US" + code = "214fe0c2-dd47-46df-b53b-66090b3c1d40" + Barcode.create!(region: region, code: code, other_attr: "xxx") + + connection.change_table "barcodes" do |t| + connection.remove_column("barcodes", "other_attr") + end + + assert_equal ["region", "code"], connection.primary_keys("barcodes") + + barcode = Barcode.first + assert_equal region, barcode.region + assert_equal code, barcode.code + ensure + Barcode.reset_column_information end def test_supports_extensions -- cgit v1.2.3 From da8e0ba03cbae33857954c0c1a228bd6dae562da Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Fri, 8 Dec 2017 13:15:04 -0500 Subject: Swap raw video width and height if angle is 90 or 270 degrees --- .../lib/active_storage/analyzer/video_analyzer.rb | 14 +++++++++++++- activestorage/test/analyzer/video_analyzer_test.rb | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/activestorage/lib/active_storage/analyzer/video_analyzer.rb b/activestorage/lib/active_storage/analyzer/video_analyzer.rb index b6fc54d917..1c144baa37 100644 --- a/activestorage/lib/active_storage/analyzer/video_analyzer.rb +++ b/activestorage/lib/active_storage/analyzer/video_analyzer.rb @@ -31,10 +31,18 @@ module ActiveStorage private def width - Integer(video_stream["width"]) if video_stream["width"] + rotated? ? raw_height : raw_width end def height + rotated? ? raw_width : raw_height + end + + def raw_width + Integer(video_stream["width"]) if video_stream["width"] + end + + def raw_height Integer(video_stream["height"]) if video_stream["height"] end @@ -52,6 +60,10 @@ module ActiveStorage end end + def rotated? + angle == 90 || angle == 270 + end + def tags @tags ||= video_stream["tags"] || {} diff --git a/activestorage/test/analyzer/video_analyzer_test.rb b/activestorage/test/analyzer/video_analyzer_test.rb index 4a3c4a8bfc..b3b9c97fe4 100644 --- a/activestorage/test/analyzer/video_analyzer_test.rb +++ b/activestorage/test/analyzer/video_analyzer_test.rb @@ -21,8 +21,8 @@ class ActiveStorage::Analyzer::VideoAnalyzerTest < ActiveSupport::TestCase blob = create_file_blob(filename: "rotated_video.mp4", content_type: "video/mp4") metadata = blob.tap(&:analyze).metadata - assert_equal 640, metadata[:width] - assert_equal 480, metadata[:height] + assert_equal 480, metadata[:width] + assert_equal 640, metadata[:height] assert_equal [4, 3], metadata[:aspect_ratio] assert_equal 5.227975, metadata[:duration] assert_equal 90, metadata[:angle] -- cgit v1.2.3 From a58543dbb1ea52f3cb0c98d054ffd7bc7a373765 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 8 Dec 2017 13:23:31 -0800 Subject: Add failing test for wrong database connection When tests are run with just `ruby`, the RAILS_ENV is set to `development` too early, and we connect to the development database rather than the test database. --- railties/test/application/test_runner_test.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index 27983c3b55..17333b6ac9 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -570,6 +570,10 @@ module ApplicationTests end def test_running_with_ruby_gets_test_env_by_default + # Subshells inherit `ENV`, so we need to ensure `RAILS_ENV` is set to + # nil before we run the tests in the test app. + re, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], nil + file = create_test_for_env("test") results = Dir.chdir(app_path) { `ruby -Ilib:test #{file}`.each_line.map { |line| JSON.parse line } @@ -577,9 +581,16 @@ module ApplicationTests assert_equal 1, results.length failures = results.first["failures"] flunk(failures.first) unless failures.empty? + + ensure + ENV["RAILS_ENV"] = re end def test_running_with_ruby_can_set_env_via_cmdline + # Subshells inherit `ENV`, so we need to ensure `RAILS_ENV` is set to + # nil before we run the tests in the test app. + re, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], nil + file = create_test_for_env("development") results = Dir.chdir(app_path) { `ruby -Ilib:test #{file} -e development`.each_line.map { |line| JSON.parse line } @@ -587,6 +598,9 @@ module ApplicationTests assert_equal 1, results.length failures = results.first["failures"] flunk(failures.first) unless failures.empty? + + ensure + ENV["RAILS_ENV"] = re end def test_rake_passes_multiple_TESTOPTS_to_minitest @@ -777,6 +791,9 @@ module ApplicationTests class EnvironmentTest < ActiveSupport::TestCase def test_environment + test_db = ActiveRecord::Base.configurations[#{env.dump}]["database"] + db_file = ActiveRecord::Base.connection_config[:database] + assert_match(test_db, db_file) assert_equal #{env.dump}, ENV["RAILS_ENV"] end end -- cgit v1.2.3 From b47eeadb364f5f7a46a5f81bb5270a800ff07c2b Mon Sep 17 00:00:00 2001 From: bogdanvlviv Date: Fri, 8 Dec 2017 23:27:18 +0200 Subject: CI against JRuby 9.1.15.0 JRuby 9.1.15.0 has been released: http://jruby.org/2017/12/07/jruby-9-1-15-0.html --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index fd3b3f9002..ac151b902d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -103,17 +103,17 @@ matrix: - "GEM=ar:postgresql POSTGRES=9.2" addons: postgresql: "9.2" - - rvm: jruby-9.1.14.0 + - rvm: jruby-9.1.15.0 jdk: oraclejdk8 env: - "GEM=ap" - - rvm: jruby-9.1.14.0 + - rvm: jruby-9.1.15.0 jdk: oraclejdk8 env: - "GEM=am,amo,aj" allow_failures: - rvm: ruby-head - - rvm: jruby-9.1.14.0 + - rvm: jruby-9.1.15.0 - env: "GEM=ac:integration" fast_finish: true -- cgit v1.2.3 From cd0283ef29eaa4a8eb9c90d6c2efaccb20012a20 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 8 Dec 2017 13:27:30 -0800 Subject: Revert "remove unnecessary `RAILS_ENV` setting" This reverts commit 9a80f52541ed2c93ebef02909ecab3aaf9127150. --- .../lib/rails/generators/rails/app/templates/test/test_helper.rb.tt | 1 + .../lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt index 6ad1f11781..52d68cc77c 100644 --- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt @@ -1,3 +1,4 @@ +ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' require 'rails/test_help' diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt index 7fa9973931..755d19ef5d 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt @@ -1,3 +1,6 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + require_relative "<%= File.join('..', options[:dummy_path], 'config/environment') -%>" <% unless options[:skip_active_record] -%> ActiveRecord::Migrator.migrations_paths = [File.expand_path("../<%= options[:dummy_path] -%>/db/migrate", __dir__)] -- cgit v1.2.3 From a50b8ea3503f5642330c1e6b94320115796b4bab Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 8 Dec 2017 13:42:01 -0800 Subject: Set the Rails environment from an environment variable Option parsing happens too late to have any impact on the Rails environment. Rails accesses the environment name and memoizes it too early in the boot process for a commandline option to have any impact on the database connection, so we'll change this test to set the environment from an environment variable (and ensure it still works when running tests with `ruby`) --- railties/lib/rails/test_unit/runner.rb | 5 +---- railties/test/application/test_runner_test.rb | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb index 45e8472f6a..de5744c662 100644 --- a/railties/lib/rails/test_unit/runner.rb +++ b/railties/lib/rails/test_unit/runner.rb @@ -12,11 +12,8 @@ module Rails class << self def attach_before_load_options(opts) - ENV["RAILS_ENV"] = "test" opts.on("--warnings", "-w", "Run with Ruby warnings enabled") {} - opts.on("-e", "--environment ENV", "Run tests in the ENV environment") { |e| - ENV["RAILS_ENV"] = e - } + opts.on("-e", "--environment ENV", "Run tests in the ENV environment") {} end def parse_options(argv) diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index 17333b6ac9..a01325fdb8 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -593,7 +593,7 @@ module ApplicationTests file = create_test_for_env("development") results = Dir.chdir(app_path) { - `ruby -Ilib:test #{file} -e development`.each_line.map { |line| JSON.parse line } + `RAILS_ENV=development ruby -Ilib:test #{file}`.each_line.map { |line| JSON.parse line } } assert_equal 1, results.length failures = results.first["failures"] -- cgit v1.2.3