aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock18
-rw-r--r--actioncable/lib/action_cable/remote_connections.rb3
-rw-r--r--actioncable/lib/action_cable/subscription_adapter/redis.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb18
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb2
-rw-r--r--actionpack/test/dispatch/cookies_test.rb32
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb9
-rw-r--r--actionview/test/ujs/public/vendor/qunit.js2
-rw-r--r--activejob/lib/active_job/queue_adapters/async_adapter.rb2
-rw-r--r--activejob/test/support/integration/test_case_helpers.rb4
-rw-r--r--activemodel/lib/active_model/errors.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb47
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb25
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb37
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb7
-rw-r--r--activerecord/lib/active_record/relation.rb3
-rw-r--r--activerecord/lib/active_record/schema.rb2
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb4
-rw-r--r--activerecord/test/cases/adapter_test.rb36
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb10
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb20
-rw-r--r--activerecord/test/cases/persistence_test.rb36
-rw-r--r--activerecord/test/cases/relation/where_test.rb19
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb25
-rw-r--r--activerecord/test/cases/timestamp_test.rb22
-rw-r--r--activestorage/.codeclimate.yml7
-rw-r--r--activestorage/app/controllers/active_storage/disk_controller.rb4
-rw-r--r--activestorage/app/models/active_storage/blob.rb2
-rw-r--r--activestorage/app/models/active_storage/filename.rb20
-rwxr-xr-xactivestorage/bin/test5
-rw-r--r--activestorage/lib/active_storage/service/azure_storage_service.rb12
-rw-r--r--activestorage/lib/active_storage/service/disk_service.rb4
-rw-r--r--activestorage/lib/active_storage/service/gcs_service.rb4
-rw-r--r--activestorage/lib/active_storage/service/s3_service.rb4
-rw-r--r--activestorage/test/controllers/disk_controller_test.rb4
-rw-r--r--activestorage/test/models/blob_test.rb9
-rw-r--r--activestorage/test/models/filename_test.rb18
-rw-r--r--activestorage/test/service/azure_storage_service_test.rb4
-rw-r--r--activestorage/test/service/disk_service_test.rb2
-rw-r--r--activestorage/test/service/gcs_service_test.rb2
-rw-r--r--activestorage/test/service/s3_service_test.rb2
-rw-r--r--activestorage/test/test_helper.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_merge.rb18
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb6
-rw-r--r--activesupport/test/lazy_load_hooks_test.rb13
-rw-r--r--guides/source/2_3_release_notes.md2
-rw-r--r--guides/source/5_0_release_notes.md8
-rw-r--r--guides/source/active_record_validations.md2
-rw-r--r--guides/source/api_app.md4
-rw-r--r--guides/source/asset_pipeline.md2
-rw-r--r--guides/source/autoloading_and_reloading_constants.md11
-rw-r--r--guides/source/command_line.md11
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md18
-rw-r--r--guides/source/debugging_rails_applications.md22
-rw-r--r--guides/source/development_dependencies_install.md83
-rw-r--r--guides/source/generators.md12
-rw-r--r--guides/source/initialization.md17
-rw-r--r--guides/source/rails_on_rack.md4
-rw-r--r--guides/source/security.md6
-rw-r--r--guides/source/upgrading_ruby_on_rails.md9
-rw-r--r--railties/lib/rails/commands/server/server_command.rb8
-rw-r--r--railties/lib/rails/dev_caching.rb1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/gitignore2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb2
-rw-r--r--railties/lib/rails/tasks/restart.rake1
-rw-r--r--railties/lib/rails/test_unit/line_filtering.rb1
-rw-r--r--railties/test/application/rake/dev_test.rb12
-rw-r--r--railties/test/application/rake/restart_test.rb9
-rw-r--r--railties/test/application/server_test.rb28
-rw-r--r--railties/test/commands/server_test.rb2
-rw-r--r--railties/test/generators/app_generator_test.rb119
-rw-r--r--railties/test/generators/plugin_generator_test.rb81
-rw-r--r--railties/test/generators/shared_generator_tests.rb114
87 files changed, 702 insertions, 502 deletions
diff --git a/.travis.yml b/.travis.yml
index 95a9099901..6c7380f050 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -114,7 +114,8 @@ notifications:
on_success: change
on_failure: always
channels:
- - "irc.freenode.org#rails-contrib"
+ # "irc.freenode.org#rails-contrib"
+ - secure: "QFKSOK7xQiWWqTzYfYm0XWoW7idzuxT57MBW9i9EASyRLEPuDwZEubKRP40Y7wPx7ylQd9lp6kJheeLnrDvvTjFbW3sWv9GDRl4WlOU8sG/Kv7MXAASXlDqzyJxxXTtzLeXz2iwY296kOBuKxKxl923eTvEGeocwH02QGo14LpQ="
campfire:
on_success: change
on_failure: always
diff --git a/Gemfile b/Gemfile
index 0b98dc1f75..412cc627b9 100644
--- a/Gemfile
+++ b/Gemfile
@@ -40,7 +40,7 @@ gem "rubocop", ">= 0.47", require: false
gem "rb-inotify", github: "matthewd/rb-inotify", branch: "close-handling", require: false
group :doc do
- gem "sdoc", "> 1.0.0.rc1", "< 2.0"
+ gem "sdoc", github: "robin850/sdoc", branch: "upgrade"
gem "redcarpet", "~> 3.2.3", platforms: :ruby
gem "w3c_validators"
gem "kindlerb", "~> 1.2.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index 2096465a8b..516b4155c1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -41,6 +41,14 @@ GIT
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
+GIT
+ remote: https://github.com/robin850/sdoc.git
+ revision: 0e340352f3ab2f196c8a8743f83c2ee286e4f71c
+ branch: upgrade
+ specs:
+ sdoc (1.0.0.rc2)
+ rdoc (~> 5.0)
+
PATH
remote: .
specs:
@@ -135,7 +143,7 @@ GEM
bcrypt (3.1.11-x86-mingw32)
beaneater (1.0.0)
benchmark-ips (2.7.2)
- blade (0.7.0)
+ blade (0.7.1)
activesupport (>= 3.0.0)
blade-qunit_adapter (~> 2.0.1)
coffee-script
@@ -145,7 +153,7 @@ GEM
faye
sprockets (>= 3.0)
thin (>= 1.6.0)
- thor (~> 0.19.1)
+ thor (>= 0.19.1)
useragent (~> 0.16.7)
blade-qunit_adapter (2.0.1)
blade-sauce_labs_plugin (0.7.2)
@@ -389,8 +397,6 @@ GEM
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
- sdoc (1.0.0.rc2)
- rdoc (~> 5.0)
selenium-webdriver (3.5.1)
childprocess (~> 0.5)
rubyzip (~> 1.0)
@@ -437,7 +443,7 @@ GEM
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
- thor (0.19.4)
+ thor (0.20.0)
thread (0.1.7)
thread_safe (0.3.6)
tilt (2.0.8)
@@ -521,7 +527,7 @@ DEPENDENCIES
resque-scheduler
rubocop (>= 0.47)
sass-rails!
- sdoc (> 1.0.0.rc1, < 2.0)
+ sdoc!
sequel
sidekiq
sneakers
diff --git a/actioncable/lib/action_cable/remote_connections.rb b/actioncable/lib/action_cable/remote_connections.rb
index a92b5e90b4..a07a517092 100644
--- a/actioncable/lib/action_cable/remote_connections.rb
+++ b/actioncable/lib/action_cable/remote_connections.rb
@@ -51,9 +51,10 @@ module ActionCable
server.connection_identifiers
end
- private
+ protected
attr_reader :server
+ private
def set_identifier_instance_vars(ids)
raise InvalidIdentifiersError unless valid_identifiers?(ids)
ids.each { |k, v| instance_variable_set("@#{k}", v) }
diff --git a/actioncable/lib/action_cable/subscription_adapter/redis.rb b/actioncable/lib/action_cable/subscription_adapter/redis.rb
index c64f55f5b7..facea944ff 100644
--- a/actioncable/lib/action_cable/subscription_adapter/redis.rb
+++ b/actioncable/lib/action_cable/subscription_adapter/redis.rb
@@ -10,7 +10,7 @@ module ActionCable
class Redis < Base # :nodoc:
prepend ChannelPrefix
- # Overwrite this factory method for redis connections if you want to use a different Redis library than Redis.
+ # Overwrite this factory method for Redis connections if you want to use a different Redis library than the redis gem.
# This is needed, for example, when using Makara proxies for distributed Redis.
cattr_accessor :redis_connector, default: ->(config) do
::Redis.new(config.slice(:url, :host, :port, :db, :password))
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 5a55ee13ee..c0913715ac 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -360,7 +360,11 @@ module ActionDispatch
@cookies.map { |k, v| "#{escape(k)}=#{escape(v)}" }.join "; "
end
- def handle_options(options) #:nodoc:
+ def handle_options(options) # :nodoc:
+ if options[:expires].respond_to?(:from_now)
+ options[:expires] = options[:expires].from_now
+ end
+
options[:path] ||= "/"
if options[:domain] == :all || options[:domain] == "all"
@@ -488,6 +492,14 @@ module ActionDispatch
def request; @parent_jar.request; end
private
+ def expiry_options(options)
+ if options[:expires].respond_to?(:from_now)
+ { expires_in: options[:expires] }
+ else
+ { expires_at: options[:expires] }
+ end
+ end
+
def parse(name, data); data; end
def commit(options); end
end
@@ -569,7 +581,7 @@ module ActionDispatch
end
def commit(options)
- options[:value] = @verifier.generate(serialize(options[:value]))
+ options[:value] = @verifier.generate(serialize(options[:value]), expiry_options(options))
raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
end
@@ -609,7 +621,7 @@ module ActionDispatch
end
def commit(options)
- options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]))
+ options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]), expiry_options(options))
raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 28809d7e67..57a5bc681e 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1851,7 +1851,7 @@ module ActionDispatch
path_types.fetch(String, []).each do |_path|
route_options = options.dup
if _path && option_path
- raise ArgumentError, "Ambiguous route definition. Both :path and the route path where specified as strings."
+ raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings."
end
to = get_to_from_path(_path, to, route_options[:action])
decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
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 4eef15b6c2..dbd27ffd32 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
@@ -21,7 +21,7 @@ module ActionDispatch
# This is the default value if the +CI+ environment variables
# is defined.
# * [+artifact+] display the screenshot in the terminal, using the terminal
- # artifact format (http://buildkite.github.io/terminal/inline-images/).
+ # artifact format (https://buildkite.github.io/terminal/inline-images/).
def take_screenshot
save_image
puts display_image
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index a551fb00a8..cb225c0f62 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -278,6 +278,11 @@ class CookiesTest < ActionController::TestCase
def encrypted_cookie
cookies.encrypted["foo"]
end
+
+ def cookie_expires_in_two_hours
+ cookies[:user_name] = { value: "assain", expires: 2.hours }
+ head :ok
+ end
end
tests TestController
@@ -1235,6 +1240,33 @@ class CookiesTest < ActionController::TestCase
assert_equal "bar", @controller.encrypted_cookie
end
+ def test_signed_cookie_with_expires_set_relatively
+ cookies.signed[:user_name] = { value: "assain", expires: 2.hours }
+
+ travel 1.hour
+ assert_equal "assain", cookies.signed[:user_name]
+
+ travel 2.hours
+ assert_nil cookies.signed[:user_name]
+ end
+
+ def test_encrypted_cookie_with_expires_set_relatively
+ cookies.encrypted[:user_name] = { value: "assain", expires: 2.hours }
+
+ travel 1.hour
+ assert_equal "assain", cookies.encrypted[:user_name]
+
+ travel 2.hours
+ assert_nil cookies.encrypted[:user_name]
+ end
+
+ def test_vanilla_cookie_with_expires_set_relatively
+ travel_to Time.utc(2017, 8, 15) do
+ get :cookie_expires_in_two_hours
+ assert_cookie_header "user_name=assain; path=/; expires=Tue, 15 Aug 2017 02:00:00 -0000"
+ end
+ end
+
private
def assert_cookie_header(expected)
header = @response.headers["Set-Cookie"]
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index 26a63c9f7d..6517cf4c99 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -26,6 +26,11 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
render plain: Rack::Utils.escape(Verifier.generate(session.to_hash))
end
+ def set_session_value_expires_in_five_hours
+ session[:foo] = "bar"
+ render plain: Rack::Utils.escape(Verifier.generate(session.to_hash, expires_in: 5.hours))
+ end
+
def get_session_value
render plain: "foo: #{session[:foo].inspect}"
end
@@ -283,7 +288,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
cookies[SessionKey] = SignedBar
- get "/set_session_value"
+ get "/set_session_value_expires_in_five_hours"
assert_response :success
cookie_body = response.body
@@ -299,7 +304,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
get "/no_session_access"
assert_response :success
- assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly",
+ assert_equal "_myapp_session=#{cookies[SessionKey]}; path=/; expires=#{expected_expiry}; HttpOnly",
headers["Set-Cookie"]
end
end
diff --git a/actionview/test/ujs/public/vendor/qunit.js b/actionview/test/ujs/public/vendor/qunit.js
index 0e279fde17..50a9e6455d 100644
--- a/actionview/test/ujs/public/vendor/qunit.js
+++ b/actionview/test/ujs/public/vendor/qunit.js
@@ -42,7 +42,7 @@ var QUnit,
* with IE 7 (and prior) where Error.prototype.toString is
* not properly implemented
*
- * Based on http://es5.github.com/#x15.11.4.4
+ * Based on https://es5.github.io/#x15.11.4.4
*
* @param {String|Error} error
* @return {String} error message
diff --git a/activejob/lib/active_job/queue_adapters/async_adapter.rb b/activejob/lib/active_job/queue_adapters/async_adapter.rb
index eec9ae08bb..ebf6f384e3 100644
--- a/activejob/lib/active_job/queue_adapters/async_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/async_adapter.rb
@@ -31,7 +31,7 @@ module ActiveJob
# jobs. Since jobs share a single thread pool, long-running jobs will block
# short-lived jobs. Fine for dev/test; bad for production.
class AsyncAdapter
- # See {Concurrent::ThreadPoolExecutor}[http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadPoolExecutor.html] for executor options.
+ # See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadPoolExecutor.html] for executor options.
def initialize(**executor_options)
@scheduler = Scheduler.new(**executor_options)
end
diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb
index 9629876a6d..f02a32a38e 100644
--- a/activejob/test/support/integration/test_case_helpers.rb
+++ b/activejob/test/support/integration/test_case_helpers.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/string/inflections"
require "support/integration/jobs_manager"
module TestCaseHelpers
@@ -30,8 +29,7 @@ module TestCaseHelpers
end
def adapter_is?(*adapter_class_symbols)
- adapter = ActiveJob::Base.queue_adapter.class.name.demodulize.chomp("Adapter").underscore
- adapter_class_symbols.map(&:to_s).include? adapter
+ adapter_class_symbols.map(&:to_s).include? ActiveJob::Base.queue_adapter_name
end
def wait_for_jobs_to_finish_for(seconds = 60)
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index c24ae4e430..971bdd08b1 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -398,21 +398,19 @@ module ActiveModel
type = options.delete(:message) if options[:message].is_a?(Symbol)
if @base.class.respond_to?(:i18n_scope)
- defaults = @base.class.lookup_ancestors.map do |klass|
- [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
- :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
+ i18n_scope = @base.class.i18n_scope.to_s
+ defaults = @base.class.lookup_ancestors.flat_map do |klass|
+ [ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
+ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
end
+ defaults << :"#{i18n_scope}.errors.messages.#{type}"
else
defaults = []
end
- defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
defaults << :"errors.attributes.#{attribute}.#{type}"
defaults << :"errors.messages.#{type}"
- defaults.compact!
- defaults.flatten!
-
key = defaults.shift
defaults = options.delete(:message) if options[:message]
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 5febb5a59f..4078345abf 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -10,6 +10,11 @@ module ActiveRecord
# Converts an arel AST to SQL
def to_sql(arel_or_sql_string, binds = [])
+ sql, _ = to_sql_and_binds(arel_or_sql_string, binds)
+ sql
+ end
+
+ def to_sql_and_binds(arel_or_sql_string, binds = []) # :nodoc:
if arel_or_sql_string.respond_to?(:ast)
unless binds.empty?
raise "Passing bind parameters with an arel AST is forbidden. " \
@@ -21,6 +26,7 @@ module ActiveRecord
[arel_or_sql_string.dup.freeze, binds]
end
end
+ private :to_sql_and_binds
# This is used in the StatementCache object. It returns an object that
# can be used to query the database repeatedly.
@@ -39,7 +45,7 @@ module ActiveRecord
# Returns an ActiveRecord::Result instance.
def select_all(arel, name = nil, binds = [], preparable: nil)
arel = arel_from_relation(arel)
- sql, binds = to_sql(arel, binds)
+ sql, binds = to_sql_and_binds(arel, binds)
if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
preparable = false
else
@@ -138,22 +144,22 @@ module ActiveRecord
#
# If the next id was calculated in advance (as in Oracle), it should be
# passed in as +id_value+.
- def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- sql, binds = to_sql(arel)
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
+ sql, binds = to_sql_and_binds(arel, binds)
value = exec_insert(sql, name, binds, pk, sequence_name)
id_value || last_inserted_id(value)
end
alias create insert
# Executes the update statement and returns the number of rows affected.
- def update(arel, name = nil)
- sql, binds = to_sql(arel)
+ def update(arel, name = nil, binds = [])
+ sql, binds = to_sql_and_binds(arel, binds)
exec_update(sql, name, binds)
end
# Executes the delete statement and returns the number of rows affected.
- def delete(arel, name = nil)
- sql, binds = to_sql(arel)
+ def delete(arel, name = nil, binds = [])
+ sql, binds = to_sql_and_binds(arel, binds)
exec_delete(sql, name, binds)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 41d93c4322..25622e34c8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -95,7 +95,7 @@ module ActiveRecord
def select_all(arel, name = nil, binds = [], preparable: nil)
if @query_cache_enabled && !locked?(arel)
arel = arel_from_relation(arel)
- sql, binds = to_sql(arel, binds)
+ sql, binds = to_sql_and_binds(arel, binds)
cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
else
super
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 9be26254b2..f17f1d35e2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/core_ext/hash/compact"
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
# The goal of this module is to move Adapter specific column
@@ -20,38 +22,6 @@ module ActiveRecord
spec
end
- # This can be overridden on an Adapter level basis to support other
- # extended datatypes (Example: Adding an array option in the
- # PostgreSQL::ColumnDumper)
- def prepare_column_options(column)
- spec = {}
-
- if limit = schema_limit(column)
- spec[:limit] = limit
- end
-
- if precision = schema_precision(column)
- spec[:precision] = precision
- end
-
- if scale = schema_scale(column)
- spec[:scale] = scale
- end
-
- default = schema_default(column) if column.has_default?
- spec[:default] = default unless default.nil?
-
- spec[:null] = "false" unless column.null
-
- if collation = schema_collation(column)
- spec[:collation] = collation
- end
-
- spec[:comment] = column.comment.inspect if column.comment.present?
-
- spec
- end
-
# Lists the valid migration options
def migration_keys # :nodoc:
column_options_keys
@@ -59,6 +29,18 @@ module ActiveRecord
deprecate :migration_keys
private
+ def prepare_column_options(column)
+ spec = {}
+ spec[:limit] = schema_limit(column)
+ spec[:precision] = schema_precision(column)
+ spec[:scale] = schema_scale(column)
+ spec[:default] = schema_default(column)
+ spec[:null] = "false" unless column.null
+ spec[:collation] = schema_collation(column)
+ spec[:comment] = column.comment.inspect if column.comment.present?
+ spec.compact!
+ spec
+ end
def default_primary_key?(column)
schema_type(column) == :bigint
@@ -98,6 +80,7 @@ module ActiveRecord
end
def schema_default(column)
+ return unless column.has_default?
type = lookup_cast_type_from_column(column)
default = type.deserialize(column.default)
if default.nil?
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 8a9c497918..1b4688a904 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -20,10 +20,6 @@ module ActiveRecord
include MySQL::ColumnDumper
include MySQL::SchemaStatements
- def update_table_definition(table_name, base) # :nodoc:
- MySQL::Table.new(table_name, base)
- end
-
##
# :singleton-method:
# By default, the Mysql2Adapter will consider all columns of type <tt>tinyint(1)</tt>
@@ -177,8 +173,7 @@ module ActiveRecord
#++
def explain(arel, binds = [])
- sql, binds = to_sql(arel, binds)
- sql = "EXPLAIN #{sql}"
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
start = Time.now
result = exec_query(sql, "EXPLAIN", binds)
elapsed = Time.now - start
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 fbe3596dda..81f7dce562 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -4,24 +4,23 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
module ColumnDumper # :nodoc:
- def prepare_column_options(column)
- spec = super
- spec[:unsigned] = "true" if column.unsigned?
-
- if supports_virtual_columns? && column.virtual?
- spec[:as] = extract_expression_for_virtual_column(column)
- spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
- spec = { type: schema_type(column).inspect }.merge!(spec)
- end
-
- spec
- end
-
def migration_keys
super + [:unsigned]
end
private
+ def prepare_column_options(column)
+ spec = super
+ spec[:unsigned] = "true" if column.unsigned?
+
+ if supports_virtual_columns? && column.virtual?
+ spec[:as] = extract_expression_for_virtual_column(column)
+ spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
+ spec = { type: schema_type(column).inspect }.merge!(spec)
+ end
+
+ spec
+ end
def default_primary_key?(column)
super && column.auto_increment? && !column.unsigned?
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 1d87d60ba9..7bebe82065 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
@@ -62,6 +62,10 @@ module ActiveRecord
end
end
+ def update_table_definition(table_name, base)
+ MySQL::Table.new(table_name, base)
+ end
+
private
CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 0dd4aac463..8db2a645af 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -5,8 +5,7 @@ module ActiveRecord
module PostgreSQL
module DatabaseStatements
def explain(arel, binds = [])
- sql, binds = to_sql(arel, binds)
- sql = "EXPLAIN #{sql}"
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
end
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 12c6603081..5031605c2a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
@@ -4,19 +4,17 @@ module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module ColumnDumper # :nodoc:
- # Adds +:array+ option to the default set
- def prepare_column_options(column)
- spec = super
- spec[:array] = "true" if column.array?
- spec
- end
-
# Adds +:array+ as a valid migration key
def migration_keys
super + [:array]
end
private
+ def prepare_column_options(column)
+ spec = super
+ spec[:array] = "true" if column.array?
+ spec
+ end
def default_primary_key?(column)
schema_type(column) == :bigserial
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 780e642f21..f258203a43 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -586,6 +586,10 @@ module ActiveRecord
[super, *order_columns].join(", ")
end
+ def update_table_definition(table_name, base) # :nodoc:
+ PostgreSQL::Table.new(table_name, base)
+ end
+
private
def schema_creation
PostgreSQL::SchemaCreation.new(self)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 3b4439fc46..878c9c8ccd 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -76,7 +76,7 @@ module ActiveRecord
primary_key: "bigserial primary key",
string: { name: "character varying" },
text: { name: "text" },
- integer: { name: "integer" },
+ integer: { name: "integer", limit: 4 },
float: { name: "float" },
decimal: { name: "decimal" },
datetime: { name: "timestamp" },
@@ -368,10 +368,6 @@ module ActiveRecord
@use_insert_returning
end
- def update_table_definition(table_name, base) #:nodoc:
- PostgreSQL::Table.new(table_name, base)
- end
-
def column_name_for_operation(operation, node) # :nodoc:
OPERATION_ALIASES.fetch(operation) { operation.downcase }
end
@@ -439,9 +435,9 @@ module ActiveRecord
end
def initialize_type_map(m = type_map)
- register_class_with_limit m, "int2", Type::Integer
- register_class_with_limit m, "int4", Type::Integer
- register_class_with_limit m, "int8", Type::Integer
+ m.register_type "int2", Type::Integer.new(limit: 2)
+ m.register_type "int4", Type::Integer.new(limit: 4)
+ m.register_type "int8", Type::Integer.new(limit: 8)
m.register_type "oid", OID::Oid.new
m.register_type "float4", Type::Float.new
m.alias_type "float8", "float4"
@@ -508,17 +504,6 @@ module ActiveRecord
load_additional_types
end
- def extract_limit(sql_type)
- case sql_type
- when /^bigint/i, /^int8/i
- 8
- when /^smallint/i
- 2
- else
- super
- end
- end
-
# Extracts the value from a PostgreSQL column default definition.
def extract_value_from_default(default)
case default
@@ -703,18 +688,20 @@ module ActiveRecord
# Use standard-conforming strings so we don't have to do the E'...' dance.
set_standard_conforming_strings
+ variables = @config.fetch(:variables, {}).stringify_keys
+
# If using Active Record's time zone support configure the connection to return
# TIMESTAMP WITH ZONE types in UTC.
- # (SET TIME ZONE does not use an equals sign like other SET variables)
- if ActiveRecord::Base.default_timezone == :utc
- execute("SET time zone 'UTC'", "SCHEMA")
- elsif @local_tz
- execute("SET time zone '#{@local_tz}'", "SCHEMA")
+ unless variables["timezone"]
+ if ActiveRecord::Base.default_timezone == :utc
+ variables["timezone"] = "UTC"
+ elsif @local_tz
+ variables["timezone"] = @local_tz
+ end
end
# SET statements from :variables config hash
# https://www.postgresql.org/docs/current/static/sql-set.html
- variables = @config[:variables] || {}
variables.map do |k, v|
if v == ":default" || v == :default
# Sets the value to the global or compile default
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 c155e7f1ac..ee84646214 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
@@ -39,6 +39,10 @@ module ActiveRecord
end
end
+ def update_table_definition(table_name, base)
+ SQLite3::Table.new(table_name, base)
+ end
+
private
def schema_creation
SQLite3::SchemaCreation.new(self)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 8c12cb09bd..327ca34e77 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -99,10 +99,6 @@ module ActiveRecord
end
end
- def update_table_definition(table_name, base) # :nodoc:
- SQLite3::Table.new(table_name, base)
- end
-
def initialize(connection, logger, connection_options, config)
super(connection, logger, config)
@@ -203,8 +199,7 @@ module ActiveRecord
#++
def explain(arel, binds = [])
- sql, binds = to_sql(arel, binds)
- sql = "EXPLAIN QUERY PLAN #{sql}"
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index caabad6055..d319978930 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -571,8 +571,7 @@ module ActiveRecord
conn = klass.connection
conn.unprepared_statement {
- sql, _ = conn.to_sql(relation.arel)
- sql
+ conn.to_sql(relation.arel)
}
end
end
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index f166c27fd9..1e121f2a09 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -39,7 +39,7 @@ module ActiveRecord
# The +info+ hash is optional, and if given is used to define metadata
# about the current schema (currently, only the schema's version):
#
- # ActiveRecord::Schema.define(version: 20380119000001) do
+ # ActiveRecord::Schema.define(version: 2038_01_19_000001) do
# ...
# end
def self.define(info = {}, &block)
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 27a1c89bd1..fd644225ca 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -243,7 +243,9 @@ HEADER
end
def remove_prefix_and_suffix(table)
- table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
+ prefix = Regexp.escape(@options[:table_name_prefix].to_s)
+ suffix = Regexp.escape(@options[:table_name_suffix].to_s)
+ table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1")
end
def ignored?(table_name)
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 6e04578576..9aaa2852d0 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -227,6 +227,38 @@ module ActiveRecord
result = @connection.select_all("SELECT * FROM posts WHERE id = #{Arel::Nodes::BindParam.new(nil).to_sql}", nil, [[nil, post.id]])
assert_equal expected.to_hash, result.to_hash
end
+
+ def test_insert_update_delete_with_legacy_binds
+ binds = [[nil, 1]]
+ bind_param = Arel::Nodes::BindParam.new(nil)
+
+ id = @connection.insert("INSERT INTO events(id) VALUES (#{bind_param.to_sql})", nil, nil, nil, nil, binds)
+ assert_equal 1, id
+
+ @connection.update("UPDATE events SET title = 'foo' WHERE id = #{bind_param.to_sql}", nil, binds)
+ result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ assert_equal({ "id" => 1, "title" => "foo" }, result.first)
+
+ @connection.delete("DELETE FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ assert_nil result.first
+ end
+
+ def test_insert_update_delete_with_binds
+ binds = [Relation::QueryAttribute.new("id", 1, Type.default_value)]
+ bind_param = Arel::Nodes::BindParam.new(nil)
+
+ id = @connection.insert("INSERT INTO events(id) VALUES (#{bind_param.to_sql})", nil, nil, nil, nil, binds)
+ assert_equal 1, id
+
+ @connection.update("UPDATE events SET title = 'foo' WHERE id = #{bind_param.to_sql}", nil, binds)
+ result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ assert_equal({ "id" => 1, "title" => "foo" }, result.first)
+
+ @connection.delete("DELETE FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ assert_nil result.first
+ end
end
def test_select_methods_passing_a_association_relation
@@ -256,11 +288,11 @@ module ActiveRecord
def test_log_invalid_encoding
error = assert_raises RuntimeError do
@connection.send :log, "SELECT 'Ñ‹' FROM DUAL" do
- raise "Ñ‹".force_encoding(Encoding::ASCII_8BIT)
+ raise "Ñ‹".dup.force_encoding(Encoding::ASCII_8BIT)
end
end
- assert_not_nil error.message
+ assert_equal "Ñ‹", error.message
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 24875c6678..2bb217a8b1 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -220,6 +220,13 @@ module ActiveRecord
end
end
+ def test_set_session_timezone
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { timezone: "America/New_York" }))
+ assert_equal "America/New_York", ActiveRecord::Base.connection.query_value("SHOW TIME ZONE")
+ end
+ end
+
def test_get_and_release_advisory_lock
lock_id = 5295901941911233559
list_advisory_locks = <<-SQL
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 383c7314e1..6bd11a5d81 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -576,8 +576,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_taking_with_a_number
+ klass = Class.new(Author) do
+ has_many :posts, -> { order(:id) }
+
+ def self.name
+ "Author"
+ end
+ end
+
# taking from unloaded Relation
- bob = Author.find(authors(:bob).id)
+ bob = klass.find(authors(:bob).id)
new_post = bob.posts.build
assert_not bob.posts.loaded?
assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
index 9200d32caf..be6dc2acb1 100644
--- a/activerecord/test/cases/migration/column_attributes_test.rb
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -45,11 +45,11 @@ module ActiveRecord
assert_nil TestModel.columns_hash["description"].limit
end
- if current_adapter?(:Mysql2Adapter)
+ if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
def test_unabstracted_database_dependent_types
- add_column :test_models, :intelligence_quotient, :tinyint
+ add_column :test_models, :intelligence_quotient, :smallint
TestModel.reset_column_information
- assert_match(/tinyint/, TestModel.columns_hash["intelligence_quotient"].sql_type)
+ assert_match(/smallint/, TestModel.columns_hash["intelligence_quotient"].sql_type)
end
end
@@ -99,7 +99,21 @@ module ActiveRecord
assert_equal 7, wealth_column.scale
end
+ # Test SQLite3 adapter specifically for decimal types with precision and scale
+ # attributes, since these need to be maintained in schema but aren't actually
+ # used in SQLite3 itself
if current_adapter?(:SQLite3Adapter)
+ def test_change_column_with_new_precision_and_scale
+ connection.add_column "test_models", "wealth", :decimal, precision: 9, scale: 7
+
+ connection.change_column "test_models", "wealth", :decimal, precision: 12, scale: 8
+ TestModel.reset_column_information
+
+ wealth_column = TestModel.columns_hash["wealth"]
+ assert_equal 12, wealth_column.precision
+ assert_equal 8, wealth_column.scale
+ end
+
def test_change_column_preserve_other_column_precision_and_scale
connection.add_column "test_models", "last_name", :string
connection.add_column "test_models", "wealth", :decimal, precision: 9, scale: 7
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index c887f54560..170fd02b6f 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -78,6 +78,16 @@ class PersistenceTest < ActiveRecord::TestCase
assert_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_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_delete_all
assert Topic.count > 0
@@ -912,13 +922,33 @@ class PersistenceTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
end
+ def test_class_level_destroy_is_affected_by_scoping
+ 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_nothing_raised { Topic.find(1) }
+ assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) }
+ end
+
def test_class_level_delete
- should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
- Topic.find(1).replies << should_be_destroyed_reply
+ should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
+ Topic.find(1).replies << should_not_be_destroyed_reply
Topic.delete(1)
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
- assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
+ assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) }
+ end
+
+ def test_class_level_delete_is_affected_by_scoping
+ should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
+ Topic.find(1).replies << should_not_be_destroyed_reply
+
+ Topic.where("1=0").scoping { Topic.delete(1) }
+ assert_nothing_raised { Topic.find(1) }
+ assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) }
end
def test_create_with_custom_timestamps
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 2fe2a67b1c..d95a54a2fe 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -109,6 +109,15 @@ module ActiveRecord
assert_equal expected.to_sql, actual.to_sql
end
+ def test_polymorphic_shallow_where_not
+ treasure = treasures(:sapphire)
+
+ expected = [price_estimates(:diamond), price_estimates(:honda)]
+ actual = PriceEstimate.where.not(estimate_of: treasure)
+
+ assert_equal expected.sort_by(&:id), actual.sort_by(&:id)
+ end
+
def test_polymorphic_nested_array_where
treasure = Treasure.new
treasure.id = 1
@@ -121,6 +130,16 @@ module ActiveRecord
assert_equal expected.to_sql, actual.to_sql
end
+ def test_polymorphic_nested_array_where_not
+ treasure = treasures(:diamond)
+ car = cars(:honda)
+
+ expected = [price_estimates(:sapphire_1), price_estimates(:sapphire_2)]
+ actual = PriceEstimate.where.not(estimate_of: [treasure, car])
+
+ assert_equal expected.sort_by(&:id), actual.sort_by(&:id)
+ end
+
def test_polymorphic_array_where_multiple_types
treasure_1 = treasures(:diamond)
treasure_2 = treasures(:sapphire)
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 01ec3e06ad..eb9b257da9 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -400,6 +400,31 @@ class SchemaDumperTest < ActiveRecord::TestCase
$stdout = original
end
+ def test_schema_dump_with_table_name_prefix_and_suffix_regexp_escape
+ original, $stdout = $stdout, StringIO.new
+ ActiveRecord::Base.table_name_prefix = "foo$"
+ ActiveRecord::Base.table_name_suffix = "$bar"
+
+ migration = CreateDogMigration.new
+ migration.migrate(:up)
+
+ output = perform_schema_dump
+ assert_no_match %r{create_table "foo\$.+\$bar"}, output
+ assert_no_match %r{add_index "foo\$.+\$bar"}, output
+ assert_no_match %r{create_table "schema_migrations"}, output
+ assert_no_match %r{create_table "ar_internal_metadata"}, output
+
+ if ActiveRecord::Base.connection.supports_foreign_keys?
+ assert_no_match %r{add_foreign_key "foo\$.+\$bar"}, output
+ assert_no_match %r{add_foreign_key "[^"]+", "foo\$.+\$bar"}, output
+ end
+ ensure
+ migration.migrate(:down)
+
+ ActiveRecord::Base.table_name_suffix = ActiveRecord::Base.table_name_prefix = ""
+ $stdout = original
+ end
+
def test_schema_dump_with_table_name_prefix_and_ignoring_tables
original, $stdout = $stdout, StringIO.new
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index ba0bc6c45d..5d3e2a175c 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -446,17 +446,6 @@ class TimestampTest < ActiveRecord::TestCase
toy = Toy.first
assert_equal ["created_at", "updated_at"], toy.send(:all_timestamp_attributes_in_model)
end
-
- def test_index_is_created_for_both_timestamps
- ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
- t.timestamps null: true, index: true
- end
-
- indexes = ActiveRecord::Base.connection.indexes("foos")
- assert_equal ["created_at", "updated_at"], indexes.flat_map(&:columns).sort
- ensure
- ActiveRecord::Base.connection.drop_table(:foos)
- end
end
class TimestampsWithoutTransactionTest < ActiveRecord::TestCase
@@ -475,4 +464,15 @@ class TimestampsWithoutTransactionTest < ActiveRecord::TestCase
assert_nil post.updated_at
end
end
+
+ def test_index_is_created_for_both_timestamps
+ ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
+ t.timestamps null: true, index: true
+ end
+
+ indexes = ActiveRecord::Base.connection.indexes("foos")
+ assert_equal ["created_at", "updated_at"], indexes.flat_map(&:columns).sort
+ ensure
+ ActiveRecord::Base.connection.drop_table(:foos)
+ end
end
diff --git a/activestorage/.codeclimate.yml b/activestorage/.codeclimate.yml
deleted file mode 100644
index 18f3dbb7b5..0000000000
--- a/activestorage/.codeclimate.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-engines:
- rubocop:
- enabled: true
-
-ratings:
- paths:
- - "**.rb"
diff --git a/activestorage/app/controllers/active_storage/disk_controller.rb b/activestorage/app/controllers/active_storage/disk_controller.rb
index b10d4e2cac..41e6d61bff 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),
- filename: params[:filename], disposition: disposition_param, content_type: params[:content_type]
+ disposition: disposition_param, content_type: params[:content_type]
else
head :not_found
end
@@ -39,7 +39,7 @@ class ActiveStorage::DiskController < ActionController::Base
end
def disposition_param
- params[:disposition].presence_in(%w( inline attachment )) || "inline"
+ params[:disposition].presence || "inline"
end
diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb
index 9f2ed1e5ac..2e627ef118 100644
--- a/activestorage/app/models/active_storage/blob.rb
+++ b/activestorage/app/models/active_storage/blob.rb
@@ -127,7 +127,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
# 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: filename, content_type: content_type
+ service.url key, expires_in: expires_in, disposition: "#{disposition}; filename=\"#{filename}\"", 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
diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb
index 6a9889addf..df21078718 100644
--- a/activestorage/app/models/active_storage/filename.rb
+++ b/activestorage/app/models/active_storage/filename.rb
@@ -9,21 +9,23 @@ class ActiveStorage::Filename
@filename = filename
end
- # Filename.new("racecar.jpg").extname # => ".jpg"
- def extname
- File.extname(@filename)
+ # Filename.new("racecar.jpg").base # => "racecar"
+ def base
+ File.basename @filename, extension_with_delimiter
end
- # Filename.new("racecar.jpg").extension # => "jpg"
- def extension
- extname.from(1)
+ # Filename.new("racecar.jpg").extension_with_delimiter # => ".jpg"
+ def extension_with_delimiter
+ File.extname @filename
end
- # Filename.new("racecar.jpg").base # => "racecar"
- def base
- File.basename(@filename, extname)
+ # Filename.new("racecar.jpg").extension_without_delimiter # => "jpg"
+ def extension_without_delimiter
+ extension_with_delimiter.from(1).to_s
end
+ alias_method :extension, :extension_without_delimiter
+
# Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg"
# Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg"
#
diff --git a/activestorage/bin/test b/activestorage/bin/test
new file mode 100755
index 0000000000..c53377cc97
--- /dev/null
+++ b/activestorage/bin/test
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+COMPONENT_ROOT = File.expand_path("..", __dir__)
+require_relative "../../tools/test"
diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb
index d0ee2273c5..895cc9c2f1 100644
--- a/activestorage/lib/active_storage/service/azure_storage_service.rb
+++ b/activestorage/lib/active_storage/service/azure_storage_service.rb
@@ -59,12 +59,16 @@ module ActiveStorage
end
end
- def url(key, expires_in:, disposition:, filename:, content_type:)
+ def url(key, expires_in:, filename:, disposition:, content_type:)
instrument :url, key do |payload|
base_url = url_for(key)
- generated_url = signer.signed_uri(URI(base_url), false, permissions: "r",
- expiry: format_expiry(expires_in), content_type: content_type,
- content_disposition: "#{disposition}; filename=\"#{filename}\"").to_s
+ generated_url = signer.signed_uri(
+ URI(base_url), false,
+ permissions: "r",
+ expiry: format_expiry(expires_in),
+ content_disposition: disposition,
+ content_type: content_type
+ ).to_s
payload[:url] = generated_url
diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb
index 1d121dcb6c..5aa8d74a5a 100644
--- a/activestorage/lib/active_storage/service/disk_service.rb
+++ b/activestorage/lib/active_storage/service/disk_service.rb
@@ -56,7 +56,7 @@ module ActiveStorage
end
end
- def url(key, expires_in:, disposition:, filename:, content_type:)
+ def url(key, expires_in:, filename:, disposition:, content_type:)
instrument :url, key do |payload|
verified_key_with_expiration = ActiveStorage.verifier.generate(key, expires_in: expires_in, purpose: :blob_key)
@@ -64,7 +64,7 @@ module ActiveStorage
if defined?(Rails.application)
Rails.application.routes.url_helpers.rails_disk_service_path \
verified_key_with_expiration,
- disposition: disposition, filename: filename, content_type: content_type
+ filename: filename, disposition: disposition, content_type: content_type
else
"/rails/active_storage/disk/#{verified_key_with_expiration}/#{filename}?disposition=#{disposition}&content_type=#{content_type}"
end
diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb
index e656f2c69f..ebf24a36d7 100644
--- a/activestorage/lib/active_storage/service/gcs_service.rb
+++ b/activestorage/lib/active_storage/service/gcs_service.rb
@@ -47,10 +47,10 @@ module ActiveStorage
end
end
- def url(key, expires_in:, disposition:, filename:, content_type:)
+ 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}; filename=\"#{filename}\"",
+ "response-content-disposition" => disposition,
"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 bef2ecbea9..e5d1e56e05 100644
--- a/activestorage/lib/active_storage/service/s3_service.rb
+++ b/activestorage/lib/active_storage/service/s3_service.rb
@@ -52,10 +52,10 @@ module ActiveStorage
end
end
- def url(key, expires_in:, disposition:, filename:, content_type:)
+ 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,
- response_content_disposition: "#{disposition}; filename=\"#{filename}\"",
+ response_content_disposition: disposition,
response_content_type: content_type
payload[:url] = generated_url
diff --git a/activestorage/test/controllers/disk_controller_test.rb b/activestorage/test/controllers/disk_controller_test.rb
index 53a086f214..eacb9bfc11 100644
--- a/activestorage/test/controllers/disk_controller_test.rb
+++ b/activestorage/test/controllers/disk_controller_test.rb
@@ -8,7 +8,7 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
blob = create_blob
get blob.service_url
- assert_equal "inline; filename=\"#{blob.filename.base}\"", @response.headers["Content-Disposition"]
+ assert_equal "inline; filename=\"hello.txt\"", @response.headers["Content-Disposition"]
assert_equal "text/plain", @response.headers["Content-Type"]
end
@@ -16,7 +16,7 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
blob = create_blob
get blob.service_url(disposition: :attachment)
- assert_equal "attachment; filename=\"#{blob.filename.base}\"", @response.headers["Content-Disposition"]
+ assert_equal "attachment; filename=\"hello.txt\"", @response.headers["Content-Disposition"]
assert_equal "text/plain", @response.headers["Content-Type"]
end
diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb
index 1c50d18994..03bf72c0de 100644
--- a/activestorage/test/models/blob_test.rb
+++ b/activestorage/test/models/blob_test.rb
@@ -41,9 +41,16 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
end
end
+ test "purge removes from external service" do
+ blob = create_blob
+
+ blob.purge
+ assert_not ActiveStorage::Blob.service.exist?(blob.key)
+ end
+
private
def expected_url_for(blob, disposition: :inline)
- query_string = { content_type: blob.content_type, disposition: disposition }.to_param
+ query_string = { content_type: blob.content_type, disposition: "#{disposition}; filename=\"#{blob.filename}\"" }.to_param
"/rails/active_storage/disk/#{ActiveStorage.verifier.generate(blob.key, expires_in: 5.minutes, purpose: :blob_key)}/#{blob.filename}?#{query_string}"
end
end
diff --git a/activestorage/test/models/filename_test.rb b/activestorage/test/models/filename_test.rb
index f1e4a467ba..88405e41c0 100644
--- a/activestorage/test/models/filename_test.rb
+++ b/activestorage/test/models/filename_test.rb
@@ -3,6 +3,24 @@
require "test_helper"
class ActiveStorage::FilenameTest < ActiveSupport::TestCase
+ test "base" do
+ assert_equal "racecar", ActiveStorage::Filename.new("racecar.jpg").base
+ assert_equal "race.car", ActiveStorage::Filename.new("race.car.jpg").base
+ assert_equal "racecar", ActiveStorage::Filename.new("racecar").base
+ end
+
+ test "extension with delimiter" do
+ assert_equal ".jpg", ActiveStorage::Filename.new("racecar.jpg").extension_with_delimiter
+ assert_equal ".jpg", ActiveStorage::Filename.new("race.car.jpg").extension_with_delimiter
+ assert_equal "", ActiveStorage::Filename.new("racecar").extension_with_delimiter
+ end
+
+ test "extension without delimiter" do
+ assert_equal "jpg", ActiveStorage::Filename.new("racecar.jpg").extension_without_delimiter
+ assert_equal "jpg", ActiveStorage::Filename.new("race.car.jpg").extension_without_delimiter
+ assert_equal "", ActiveStorage::Filename.new("racecar").extension_without_delimiter
+ end
+
test "sanitize" do
"%$|:;/\t\r\n\\".each_char do |character|
filename = ActiveStorage::Filename.new("foo#{character}bar.pdf")
diff --git a/activestorage/test/service/azure_storage_service_test.rb b/activestorage/test/service/azure_storage_service_test.rb
index dac154cf11..4729bdfbc5 100644
--- a/activestorage/test/service/azure_storage_service_test.rb
+++ b/activestorage/test/service/azure_storage_service_test.rb
@@ -11,9 +11,9 @@ if SERVICE_CONFIGURATIONS[:azure]
test "signed URL generation" do
url = @service.url(FIXTURE_KEY, expires_in: 5.minutes,
- disposition: :inline, filename: "avatar.png", content_type: "image/png")
+ disposition: "inline; filename=\"avatar.png\"", filename: "avatar.png", content_type: "image/png")
- assert_match(/(\S+)&rsct=image%2Fpng&rscd=inline%3B\+filename%3D%22avatar.png/, url)
+ assert_match(/(\S+)&rscd=inline%3B\+filename%3D%22avatar.png%22&rsct=image%2Fpng/, url)
assert_match SERVICE_CONFIGURATIONS[:azure][:container], url
end
end
diff --git a/activestorage/test/service/disk_service_test.rb b/activestorage/test/service/disk_service_test.rb
index 835a3a2971..f57e44536a 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\?.+disposition=inline/,
- @service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: :inline, filename: "avatar.png", content_type: "image/png"))
+ @service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: "inline; filename=\"avatar.png\"", filename: "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 5f53f61baf..f664cee90b 100644
--- a/activestorage/test/service/gcs_service_test.rb
+++ b/activestorage/test/service/gcs_service_test.rb
@@ -37,7 +37,7 @@ if SERVICE_CONFIGURATIONS[:gcs]
"&response-content-disposition=inline%3B+filename%3D%22test.txt%22" +
"&response-content-type=text%2Fplain"
- assert_equal url, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain")
+ assert_equal url, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: "inline; filename=\"test.txt\"", filename: "test.txt", content_type: "text/plain")
end
end
end
diff --git a/activestorage/test/service/s3_service_test.rb b/activestorage/test/service/s3_service_test.rb
index 98f520741d..c07d6396b1 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", content_type: "image/png")
+ disposition: "inline; filename=\"avatar.png\"", filename: "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/test_helper.rb b/activestorage/test/test_helper.rb
index 0bb3a925b8..2a969fa326 100644
--- a/activestorage/test/test_helper.rb
+++ b/activestorage/test/test_helper.rb
@@ -32,7 +32,7 @@ end
require "tmpdir"
ActiveStorage::Blob.service = ActiveStorage::Service::DiskService.new(root: Dir.mktmpdir("active_storage_tests"))
-ActiveStorage::Service.logger = ActiveSupport::Logger.new(STDOUT)
+ActiveStorage::Service.logger = ActiveSupport::Logger.new(nil)
ActiveStorage.verifier = ActiveSupport::MessageVerifier.new("Testing")
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
index f92a2f1c2a..9bc50b7bc6 100644
--- a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
+++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
@@ -21,20 +21,14 @@ class Hash
# Same as +deep_merge+, but modifies +self+.
def deep_merge!(other_hash, &block)
- other_hash.each_pair do |current_key, other_value|
- this_value = self[current_key]
-
- self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
- this_value.deep_merge(other_value, &block)
+ merge!(other_hash) do |key, this_val, other_val|
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
+ this_val.deep_merge(other_val, &block)
+ elsif block_given?
+ block.call(key, this_val, other_val)
else
- if block_given? && key?(current_key)
- block.call(current_key, this_value, other_value)
- else
- other_value
- end
+ other_val
end
end
-
- self
end
end
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index c124416595..dc8080c469 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -40,7 +40,7 @@ module ActiveSupport
# * <tt>:run_once</tt> - Given +block+ will run only once.
def on_load(name, options = {}, &block)
@loaded[name].each do |base|
- execute_hook(base, options, block)
+ execute_hook(name, base, options, block)
end
@load_hooks[name] << [block, options]
@@ -49,7 +49,7 @@ module ActiveSupport
def run_load_hooks(name, base = Object)
@loaded[name] << base
@load_hooks[name].each do |hook, options|
- execute_hook(base, options, hook)
+ execute_hook(name, base, options, hook)
end
end
@@ -63,7 +63,7 @@ module ActiveSupport
end
end
- def execute_hook(base, options, block)
+ def execute_hook(name, base, options, block)
with_execution_control(name, block, options[:run_once]) do
if options[:yield]
block.call(base)
diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb
index c161005100..721d44d0c1 100644
--- a/activesupport/test/lazy_load_hooks_test.rb
+++ b/activesupport/test/lazy_load_hooks_test.rb
@@ -22,14 +22,19 @@ class LazyLoadHooksTest < ActiveSupport::TestCase
def test_basic_hook_with_two_registrations_only_once
i = 0
- ActiveSupport.on_load(:basic_hook_with_two_once, run_once: true) do
+ block = proc { i += incr }
+ ActiveSupport.on_load(:basic_hook_with_two_once, run_once: true, &block)
+ ActiveSupport.on_load(:basic_hook_with_two_once) do
i += incr
end
- assert_equal 0, i
- ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(2))
+
+ ActiveSupport.on_load(:different_hook, run_once: true, &block)
+ ActiveSupport.run_load_hooks(:different_hook, FakeContext.new(2))
assert_equal 2, i
+ ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(2))
+ assert_equal 6, i
ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(5))
- assert_equal 2, i
+ assert_equal 11, i
end
def test_hook_registered_after_run
diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md
index 6976848e95..3f5a3c7ade 100644
--- a/guides/source/2_3_release_notes.md
+++ b/guides/source/2_3_release_notes.md
@@ -10,7 +10,7 @@ Rails 2.3 delivers a variety of new and improved features, including pervasive R
Application Architecture
------------------------
-There are two major changes in the architecture of Rails applications: complete integration of the [Rack](http://rack.github.io/) modular web server interface, and renewed support for Rails Engines.
+There are two major changes in the architecture of Rails applications: complete integration of the [Rack](https://rack.github.io/) modular web server interface, and renewed support for Rails Engines.
### Rack Integration
diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md
index 5f4be07351..6d53e1c2b4 100644
--- a/guides/source/5_0_release_notes.md
+++ b/guides/source/5_0_release_notes.md
@@ -55,7 +55,7 @@ information.
### API Applications
Rails can now be used to create slimmed down API only applications.
-This is useful for creating and serving APIs similar to [Twitter](https://dev.twitter.com) or [GitHub](http://developer.github.com) API,
+This is useful for creating and serving APIs similar to [Twitter](https://dev.twitter.com) or [GitHub](https://developer.github.com) API,
that can be used to serve public facing, as well as, for custom applications.
You can generate a new api Rails app using:
@@ -74,11 +74,11 @@ This will do three main things:
Controller modules that provide functionalities primarily used by browser
applications.
- Configure the generators to skip generating views, helpers and assets when
- you generate a new resource.
+ you generate a new resource.
-The application provides a base for APIs,
+The application provides a base for APIs,
that can then be [configured to pull in functionality](api_app.html) as suitable for the application's needs.
-
+
See the [Using Rails for API-only Applications](api_app.html) guide for more
information.
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index a086363cf1..e9157f3db1 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -892,7 +892,7 @@ Conditional Validation
Sometimes it will make sense to validate an object only when a given predicate
is satisfied. You can do that by using the `:if` and `:unless` options, which
-can take a symbol, a string, a `Proc` or an `Array`. You may use the `:if`
+can take a symbol, a `Proc` or an `Array`. You may use the `:if`
option when you want to specify when the validation **should** happen. If you
want to specify when the validation **should not** happen, then you may use the
`:unless` option.
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
index 64200ec242..5ec79de8a0 100644
--- a/guides/source/api_app.md
+++ b/guides/source/api_app.md
@@ -18,7 +18,7 @@ What is an API Application?
Traditionally, when people said that they used Rails as an "API", they meant
providing a programmatically accessible API alongside their web application.
-For example, GitHub provides [an API](http://developer.github.com) that you
+For example, GitHub provides [an API](https://developer.github.com) that you
can use from your own custom clients.
With the advent of client-side frameworks, more developers are using Rails to
@@ -94,7 +94,7 @@ Handled at the Action Pack layer:
means not having to spend time thinking about how to model your API in terms
of HTTP.
- URL Generation: The flip side of routing is URL generation. A good API based
- on HTTP includes URLs (see [the GitHub Gist API](http://developer.github.com/v3/gists/)
+ on HTTP includes URLs (see [the GitHub Gist API](https://developer.github.com/v3/gists/)
for an example).
- Header and Redirection Responses: `head :no_content` and
`redirect_to user_url(current_user)` come in handy. Sure, you could manually
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 383e972f74..f148cef24f 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -1069,7 +1069,7 @@ Customizing the Pipeline
### CSS Compression
One of the options for compressing CSS is YUI. The [YUI CSS
-compressor](http://yui.github.io/yuicompressor/css.html) provides
+compressor](https://yui.github.io/yuicompressor/css.html) provides
minification.
The following line enables YUI compression, and requires the `yui-compressor`
diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md
index 05743ee4ce..c62194faf4 100644
--- a/guides/source/autoloading_and_reloading_constants.md
+++ b/guides/source/autoloading_and_reloading_constants.md
@@ -475,12 +475,21 @@ it is (edited):
```
$ bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
.../app/assets
+.../app/channels
.../app/controllers
+.../app/controllers/concerns
.../app/helpers
+.../app/jobs
.../app/mailers
.../app/models
-.../app/controllers/concerns
.../app/models/concerns
+.../activestorage/app/assets
+.../activestorage/app/controllers
+.../activestorage/app/javascript
+.../activestorage/app/jobs
+.../activestorage/app/models
+.../actioncable/app/assets
+.../actionview/app/assets
.../test/mailers/previews
```
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 8ae01286e4..2cd8e02a77 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -645,13 +645,16 @@ $ cat config/database.yml
# Configure Using Gemfile
# gem 'pg'
#
-development:
+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: gitapp_development
- pool: 5
- username: gitapp
- password:
...
...
```
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index 2f2962a3e6..7424818757 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -13,7 +13,7 @@ After reading this guide, you will know:
* How to contribute to the Ruby on Rails documentation.
* How to contribute to the Ruby on Rails code.
-Ruby on Rails is not "someone else's framework." Over the years, hundreds of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches.
+Ruby on Rails is not "someone else's framework." Over the years, thousands of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches.
As mentioned in [Rails'
README](https://github.com/rails/rails/blob/master/README.md), everyone interacting in Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](http://rubyonrails.org/conduct/).
@@ -92,7 +92,7 @@ For starters, it helps just to verify bug reports. Can you reproduce the reporte
If an issue is very vague, can you help narrow it down to something more specific? Maybe you can provide additional information to help reproduce a bug, or help by eliminating needless steps that aren't required to demonstrate the problem.
-If you find a bug report without a test, it's very useful to contribute a failing test. This is also a great way to get started exploring the source code: looking at the existing test files will teach you how to write more tests. New tests are best contributed in the form of a patch, as explained later on in the "Contributing to the Rails Code" section.
+If you find a bug report without a test, it's very useful to contribute a failing test. This is also a great way to get started exploring the source code: looking at the existing test files will teach you how to write more tests. New tests are best contributed in the form of a patch, as explained later on in the "[Contributing to the Rails Code](#contributing-to-the-rails-code)" section.
Anything you can do to make bug reports more succinct or easier to reproduce helps folks trying to write code to fix those bugs - whether you end up writing the code yourself or not.
@@ -164,11 +164,11 @@ NOTE: The instructions are for Rails > 4. The Redcarpet Gem doesn't work with JR
Translation efforts we know about (various versions):
* **Italian**: [https://github.com/rixlabs/docrails](https://github.com/rixlabs/docrails)
-* **Spanish**: [http://wiki.github.com/gramos/docrails](http://wiki.github.com/gramos/docrails)
-* **Polish**: [https://github.com/apohllo/docrails/tree/master](https://github.com/apohllo/docrails/tree/master)
+* **Spanish**: [https://github.com/gramos/docrails/wiki](https://github.com/gramos/docrails/wiki)
+* **Polish**: [https://github.com/apohllo/docrails](https://github.com/apohllo/docrails)
* **French** : [https://github.com/railsfrance/docrails](https://github.com/railsfrance/docrails)
* **Czech** : [https://github.com/rubyonrails-cz/docrails/tree/czech](https://github.com/rubyonrails-cz/docrails/tree/czech)
-* **Turkish** : [https://github.com/ujk/docrails/tree/master](https://github.com/ujk/docrails/tree/master)
+* **Turkish** : [https://github.com/ujk/docrails](https://github.com/ujk/docrails)
* **Korean** : [https://github.com/rorlakr/rails-guides](https://github.com/rorlakr/rails-guides)
* **Simplified Chinese** : [https://github.com/ruby-china/guides](https://github.com/ruby-china/guides)
* **Traditional Chinese** : [https://github.com/docrails-tw/guides](https://github.com/docrails-tw/guides)
@@ -184,7 +184,7 @@ To move on from submitting bugs to helping resolve existing issues or contributi
#### The Easy Way
-The easiest and recommended way to get a development environment ready to hack is to use the [Rails development box](https://github.com/rails/rails-dev-box).
+The easiest and recommended way to get a development environment ready to hack is to use the [rails-dev-box](https://github.com/rails/rails-dev-box).
#### The Hard Way
@@ -324,7 +324,7 @@ file.
#### Testing Active Record
-First, create the databases you'll need. You can find a list of the required
+First, create the databases you'll need. You can find a list of the required
table names, usernames, and passwords in `activerecord/test/config.example.yml`.
For MySQL and PostgreSQL, running the SQL statements `create database
@@ -480,7 +480,7 @@ Navigate to the Rails [GitHub repository](https://github.com/rails/rails) and pr
Add the new remote to your local repository on your local machine:
```bash
-$ git remote add mine https://github.com:<your user name>/rails.git
+$ git remote add mine https://github.com/<your user name>/rails.git
```
Push to your remote:
@@ -554,7 +554,7 @@ is the open source life.
If it's been over a week, and you haven't heard anything, you might want to try
and nudge things along. You can use the [rubyonrails-core mailing
-list](http://groups.google.com/group/rubyonrails-core/) for this. You can also
+list](https://groups.google.com/forum/#!forum/rubyonrails-core) for this. You can also
leave another comment on the pull request.
While you're waiting for feedback on your pull request, open up a few other
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index 7ea3646c30..99bc7c5fb5 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -186,21 +186,17 @@ end
Here's an example of the log generated when this controller action is executed:
```
-Processing ArticlesController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
- Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl
-vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
- Parameters: {"commit"=>"Create", "article"=>{"title"=>"Debugging Rails",
- "body"=>"I'm learning how to print in logs!!!", "published"=>"0"},
- "authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"articles"}
-New article: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!",
- "published"=>false, "created_at"=>nil}
+Started POST "/articles" for 127.0.0.1 at 2017-08-20 20:53:10 +0900
+Processing by ArticlesController#create as HTML
+ Parameters: {"utf8"=>"✓", "authenticity_token"=>"xhuIbSBFytHCE1agHgvrlKnSVIOGD6jltW2tO+P6a/ACjQ3igjpV4OdbsZjIhC98QizWH9YdKokrqxBCJrtoqQ==", "article"=>{"title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>"0"}, "commit"=>"Create Article"}
+New article: {"id"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>false, "created_at"=>nil, "updated_at"=>nil}
Article should be valid: true
- Article Create (0.000443) INSERT INTO "articles" ("updated_at", "title", "body", "published",
- "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails',
- 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
+ (0.1ms) BEGIN
+ SQL (0.4ms) INSERT INTO "articles" ("title", "body", "published", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["title", "Debugging Rails"], ["body", "I'm learning how to print in logs!!!"], ["published", "f"], ["created_at", "2017-08-20 11:53:10.010435"], ["updated_at", "2017-08-20 11:53:10.010435"]]
+ (0.3ms) COMMIT
The article was saved and now the user is going to be redirected...
-Redirected to # Article:0x20af760>
-Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/articles]
+Redirected to http://localhost:3000/articles/1
+Completed 302 Found in 4ms (ActiveRecord: 0.8ms)
```
Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels to avoid filling your production logs with useless trivia.
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index c57efd6362..5a9729bb0f 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -21,24 +21,25 @@ The easiest and recommended way to get a development environment ready to hack i
The Hard Way
------------
-In case you can't use the Rails development box, see section below, these are the steps to manually build a development box for Ruby on Rails core development.
+In case you can't use the Rails development box, see the steps below to manually
+build a development box for Ruby on Rails core development.
### Install Git
-Ruby on Rails uses Git for source code control. The [Git homepage](http://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git:
+Ruby on Rails uses Git for source code control. The [Git homepage](https://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git:
-* [Try Git course](http://try.github.io/) is an interactive course that will teach you the basics.
-* The [official Documentation](http://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git.
-* [Everyday Git](http://schacon.github.io/git/everyday.html) will teach you just enough about Git to get by.
-* [GitHub](http://help.github.com) offers links to a variety of Git resources.
-* [Pro Git](http://git-scm.com/book) is an entire book about Git with a Creative Commons license.
+* [Try Git course](https://try.github.io/) is an interactive course that will teach you the basics.
+* The [official Documentation](https://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git.
+* [Everyday Git](https://schacon.github.io/git/everyday.html) will teach you just enough about Git to get by.
+* [GitHub](https://help.github.com/) offers links to a variety of Git resources.
+* [Pro Git](https://git-scm.com/book) is an entire book about Git with a Creative Commons license.
### Clone the Ruby on Rails Repository
Navigate to the folder where you want the Ruby on Rails source code (it will create its own `rails` subdirectory) and run:
```bash
-$ git clone git://github.com/rails/rails.git
+$ git clone https://github.com/rails/rails.git
$ cd rails
```
@@ -62,7 +63,7 @@ $ sudo apt-get install sqlite3 libsqlite3-dev
If you are on Fedora or CentOS, you're done with
```bash
-$ sudo yum install libsqlite3x libsqlite3x-devel
+$ sudo yum install libsqlite3x libsqlite3x-devel
```
If you are on Arch Linux, you will need to run:
@@ -96,7 +97,7 @@ This command will install all dependencies except the MySQL and PostgreSQL Ruby
NOTE: If you would like to run the tests that use memcached, you need to ensure that you have it installed and running.
-You can use [Homebrew](http://brew.sh/) to install memcached on OS X:
+You can use [Homebrew](https://brew.sh/) to install memcached on macOS:
```bash
$ brew install memcached
@@ -181,7 +182,7 @@ The Active Record test suite requires a custom config file: `activerecord/test/c
To be able to run the suite for MySQL and PostgreSQL we need their gems. Install
first the servers, their client libraries, and their development files.
-On OS X, you can run:
+On macOS, you can run:
```bash
$ brew install mysql
@@ -190,7 +191,7 @@ $ brew install postgresql
Follow the instructions given by Homebrew to start these.
-In Ubuntu just run:
+On Ubuntu, just run:
```bash
$ sudo apt-get install mysql-server libmysqlclient-dev
@@ -260,34 +261,34 @@ with your development account, on Linux or BSD, you just have to run:
$ sudo -u postgres createuser --superuser $USER
```
-and for OS X:
+and for macOS:
```bash
$ createuser --superuser $USER
```
-Then you need to create the test databases with
+Then, you need to create the test databases with:
```bash
$ cd activerecord
$ bundle exec rake db:postgresql:build
```
-It is possible to build databases for both PostgreSQL and MySQL with
+It is possible to build databases for both PostgreSQL and MySQL with:
```bash
$ cd activerecord
$ bundle exec rake db:create
```
-You can cleanup the databases using
+You can cleanup the databases using:
```bash
$ cd activerecord
$ bundle exec rake db:drop
```
-NOTE: Using the rake task to create the test databases ensures they have the correct character set and collation.
+NOTE: Using the Rake task to create the test databases ensures they have the correct character set and collation.
NOTE: You'll see the following warning (or localized warning) during activating HStore extension in PostgreSQL 9.1.x or earlier: "WARNING: => is deprecated as an operator".
@@ -299,11 +300,11 @@ Action Cable uses Redis as its default subscriptions adapter ([read more](action
#### Install Redis From Source
-Redis' documentation discourage installations with package managers as those are usually outdated. Installing from source and bringing the server up is straight forward and well documented on [Redis' documentation](http://redis.io/download#installation).
+Redis' documentation discourage installations with package managers as those are usually outdated. Installing from source and bringing the server up is straight forward and well documented on [Redis' documentation](https://redis.io/download#installation).
#### Install Redis From Package Manager
-On OS X, you can run:
+On macOS, you can run:
```bash
$ brew install redis
@@ -311,7 +312,7 @@ $ brew install redis
Follow the instructions given by Homebrew to start these.
-In Ubuntu just run:
+On Ubuntu, just run:
```bash
$ sudo apt-get install redis-server
@@ -323,7 +324,7 @@ On Fedora or CentOS (requires EPEL enabled), just run:
$ sudo yum install redis
```
-If you are running Arch Linux just run:
+If you are running Arch Linux, just run:
```bash
$ sudo pacman -S redis
@@ -335,3 +336,43 @@ FreeBSD users will have to run the following:
```bash
# portmaster databases/redis
```
+
+### Active Storage Setup
+
+When working on Active Storage, it is important to note that you need to
+install its JavaScript dependencies while working on that section of the
+codebase. In order to install these dependencies, it is necessary to
+have Yarn, a Node.js package manager, available on your system. A
+prerequisite for installing this package manager is that
+[Node.js](https://nodejs.org) is installed.
+
+
+On macOS, you can run:
+
+```bash
+brew install yarn
+```
+
+On Ubuntu, you can run:
+
+```bash
+curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+
+sudo apt-get update && sudo apt-get install yarn
+```
+
+On Fedora or CentOS, just run:
+
+```bash
+sudo wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo
+
+sudo yum install yarn
+```
+
+Finally, after installing Yarn, you will need to run the following
+command inside of the `activestorage` directory to install the dependencies:
+
+```bash
+yarn install
+```
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 37af7a1310..b7b8262e4a 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -90,13 +90,15 @@ $ bin/rails generate generator initializer
create lib/generators/initializer/initializer_generator.rb
create lib/generators/initializer/USAGE
create lib/generators/initializer/templates
+ invoke test_unit
+ create test/lib/generators/initializer_generator_test.rb
```
This is the generator just created:
```ruby
class InitializerGenerator < Rails::Generators::NamedBase
- source_root File.expand_path("templates", __dir__)
+ source_root File.expand_path('templates', __dir__)
end
```
@@ -122,7 +124,7 @@ And now let's change the generator to copy this template when invoked:
```ruby
class InitializerGenerator < Rails::Generators::NamedBase
- source_root File.expand_path("templates", __dir__)
+ source_root File.expand_path('templates', __dir__)
def copy_initializer_file
copy_file "initializer.rb", "config/initializers/#{file_name}.rb"
@@ -241,6 +243,8 @@ $ bin/rails generate generator rails/my_helper
create lib/generators/rails/my_helper/my_helper_generator.rb
create lib/generators/rails/my_helper/USAGE
create lib/generators/rails/my_helper/templates
+ invoke test_unit
+ create test/lib/generators/rails/my_helper_generator_test.rb
```
After that, we can delete both the `templates` directory and the `source_root`
@@ -510,13 +514,13 @@ Available options are:
Any additional options passed to this method are put on the end of the line:
```ruby
-gem "devise", git: "git://github.com/plataformatec/devise", branch: "master"
+gem "devise", git: "https://github.com/plataformatec/devise.git", branch: "master"
```
The above code will put the following line into `Gemfile`:
```ruby
-gem "devise", git: "git://github.com/plataformatec/devise", branch: "master"
+gem "devise", git: "https://github.com/plataformatec/devise.git", branch: "master"
```
### `gem_group`
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index ccad10f07d..0cfabe2a66 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -99,13 +99,15 @@ configure the load path for your Gemfile's dependencies.
A standard Rails application depends on several gems, specifically:
+* actioncable
* actionmailer
* actionpack
* actionview
+* activejob
* activemodel
* activerecord
+* activestorage
* activesupport
-* activejob
* arel
* builder
* bundler
@@ -131,7 +133,7 @@ Once `config/boot.rb` has finished, the next file that is required is
`ARGV` array simply contains `server` which will be passed over:
```ruby
-require "rails/command"
+require_relative "command"
aliases = {
"g" => "generate",
@@ -170,7 +172,7 @@ module Rails::Command
namespace = namespace.to_s
namespace = "help" if namespace.blank? || HELP_MAPPINGS.include?(namespace)
namespace = "version" if %w( -v --version ).include? namespace
-
+
if command = find_by_namespace(namespace)
command.perform(namespace, args, config)
else
@@ -189,7 +191,7 @@ module Rails
class ServerCommand < Base # :nodoc:
def perform
set_application_directory!
-
+
Rails::Server.new.tap do |server|
# Require application after server sets environment to propagate
# the --environment option.
@@ -311,7 +313,7 @@ def parse!(args)
args, options = args.dup, {}
option_parser(options).parse! args
-
+
options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development"
options[:server] = args.shift
options
@@ -366,11 +368,11 @@ private
def log_to_stdout
wrapped_app # touch the app so the logger is set up
-
+
console = ActiveSupport::Logger.new(STDOUT)
console.formatter = Rails.logger.formatter
console.level = Rails.logger.level
-
+
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT)
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
end
@@ -537,6 +539,7 @@ require "rails"
action_mailer/railtie
active_job/railtie
action_cable/engine
+ active_storage/engine
rails/test_unit/railtie
sprockets/railtie
).each do |railtie|
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index cef8450ee4..aa1476ecc0 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -125,7 +125,7 @@ use ActionDispatch::Flash
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
-run MyApp.application.routes
+run MyApp::Application.routes
```
The default middlewares shown here (and some others) are each summarized in the [Internal Middlewares](#internal-middleware-stack) section, below.
@@ -302,7 +302,7 @@ Resources
### Learning Rack
-* [Official Rack Website](http://rack.github.io)
+* [Official Rack Website](https://rack.github.io)
* [Introducing Rack](http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html)
### Understanding Middlewares
diff --git a/guides/source/security.md b/guides/source/security.md
index 56969c7aea..7736a4b224 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -1025,7 +1025,7 @@ Here is a list of common headers:
* **X-Content-Type-Options:** _'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file.
* **X-Content-Security-Policy:** [A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html)
* **Access-Control-Allow-Origin:** Used to control which sites are allowed to bypass same origin policies and send cross-origin requests.
-* **Strict-Transport-Security:** [Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
+* **Strict-Transport-Security:** [Used to control if the browser is allowed to only access a site over a secure connection](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
Environmental Security
----------------------
@@ -1060,7 +1060,7 @@ Additional Resources
The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here:
-* Subscribe to the Rails security [mailing list](http://groups.google.com/group/rubyonrails-security).
-* [Brakeman - Rails Security Scanner](http://brakemanscanner.org/) - To perform static security analysis for Rails applications.
+* Subscribe to the Rails security [mailing list](https://groups.google.com/forum/#!forum/rubyonrails-security).
+* [Brakeman - Rails Security Scanner](https://brakemanscanner.org/) - To perform static security analysis for Rails applications.
* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too).
* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet).
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 49a0584d51..651b86275a 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -397,6 +397,15 @@ When using Ruby 2.4, you can preserve the timezone of the receiver when calling
ActiveSupport.to_time_preserves_timezone = false
+### Changes with JSON/JSONB serialization
+
+In Rails 5.0, how JSON/JSONB attributes are serialized and deserialized changed. Now, if
+you set a column equal to a `String`, Active Record will no longer turn that string
+into a `Hash`, and will instead only return the string. This is not limited to code
+interacting with models, but also affects `:default` column settings in `db/schema.rb`.
+It is recommended that you do not set columns equal to a `String`, but pass a `Hash`
+instead, which will be converted to and from a JSON string automatically.
+
Upgrading from Rails 4.1 to Rails 4.2
-------------------------------------
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 88e5698714..785265d766 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -126,6 +126,7 @@ module Rails
desc: "Specifies the PID file."
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
def initialize(args = [], local_options = {}, config = {})
@original_options = local_options
@@ -136,6 +137,7 @@ module Rails
def perform
set_application_directory!
+ prepare_restart
Rails::Server.new(server_options).tap do |server|
# Require application after server sets environment to propagate
# the --environment option.
@@ -222,7 +224,7 @@ module Rails
end
def restart_command
- "bin/rails server #{@server} #{@original_options.join(" ")}"
+ "bin/rails server #{@server} #{@original_options.join(" ")} --restart"
end
def pid
@@ -232,6 +234,10 @@ module Rails
def self.banner(*)
"rails server [puma, thin etc] [options]"
end
+
+ def prepare_restart
+ FileUtils.rm_f(options[:pid]) if options[:restart]
+ end
end
end
end
diff --git a/railties/lib/rails/dev_caching.rb b/railties/lib/rails/dev_caching.rb
index efb523de4c..ff629b2527 100644
--- a/railties/lib/rails/dev_caching.rb
+++ b/railties/lib/rails/dev_caching.rb
@@ -19,7 +19,6 @@ module Rails
end
FileUtils.touch "tmp/restart.txt"
- FileUtils.rm_f("tmp/pids/server.pid")
end
def enable_by_argument(caching)
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml
index 8bc8735a8e..2a67bdca25 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml
@@ -7,7 +7,7 @@
# gem 'activerecord-jdbcmysql-adapter'
#
# And be sure to use new-style password hashing:
-# http://dev.mysql.com/doc/refman/5.7/en/old-client.html
+# https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
adapter: mysql
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
index 269af1470d..04afaa0596 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
@@ -7,7 +7,7 @@
# gem 'mysql2'
#
# And be sure to use new-style password hashing:
-# http://dev.mysql.com/doc/refman/5.7/en/old-client.html
+# https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
adapter: mysql2
diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore
index 8c7cad74ed..e15863d860 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/gitignore
+++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore
@@ -2,8 +2,10 @@
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/
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
index a0bab22764..2fa05a9146 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
@@ -17,6 +17,7 @@ Minitest.backtrace_filter = Minitest::BacktraceFilter.new
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__)
@@ -24,3 +25,4 @@ if ActiveSupport::TestCase.respond_to?(:fixture_path=)
ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
ActiveSupport::TestCase.fixtures :all
end
+<% end -%>
diff --git a/railties/lib/rails/tasks/restart.rake b/railties/lib/rails/tasks/restart.rake
index 5b6bb8ddd6..074e3e89a1 100644
--- a/railties/lib/rails/tasks/restart.rake
+++ b/railties/lib/rails/tasks/restart.rake
@@ -5,6 +5,5 @@ task :restart do
verbose(false) do
mkdir_p "tmp"
touch "tmp/restart.txt"
- rm_f "tmp/pids/server.pid"
end
end
diff --git a/railties/lib/rails/test_unit/line_filtering.rb b/railties/lib/rails/test_unit/line_filtering.rb
index e8b891d07b..f8ca77fe4a 100644
--- a/railties/lib/rails/test_unit/line_filtering.rb
+++ b/railties/lib/rails/test_unit/line_filtering.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "method_source"
require "rails/test_unit/runner"
module Rails
diff --git a/railties/test/application/rake/dev_test.rb b/railties/test/application/rake/dev_test.rb
index d56f315455..b25593ee1c 100644
--- a/railties/test/application/rake/dev_test.rb
+++ b/railties/test/application/rake/dev_test.rb
@@ -32,12 +32,16 @@ module ApplicationTests
end
end
- test "dev:cache removes server.pid also" do
+ test "dev:cache touches tmp/restart.txt" do
Dir.chdir(app_path) do
- FileUtils.mkdir_p("tmp/pids")
- FileUtils.touch("tmp/pids/server.pid")
`rails dev:cache`
- assert_not File.exist?("tmp/pids/server.pid")
+ assert File.exist?("tmp/restart.txt")
+
+ prev_mtime = File.mtime("tmp/restart.txt")
+ sleep(1)
+ `rails dev:cache`
+ curr_mtime = File.mtime("tmp/restart.txt")
+ assert_not_equal prev_mtime, curr_mtime
end
end
end
diff --git a/railties/test/application/rake/restart_test.rb b/railties/test/application/rake/restart_test.rb
index bf06f4c522..ed96dcb6b1 100644
--- a/railties/test/application/rake/restart_test.rb
+++ b/railties/test/application/rake/restart_test.rb
@@ -35,15 +35,6 @@ module ApplicationTests
assert File.exist?("tmp/restart.txt")
end
end
-
- test "rails restart removes server.pid also" do
- Dir.chdir(app_path) do
- FileUtils.mkdir_p("tmp/pids")
- FileUtils.touch("tmp/pids/server.pid")
- `bin/rails restart`
- assert_not File.exist?("tmp/pids/server.pid")
- end
- end
end
end
end
diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb
index 97b3b4f6b3..6db9a3b9e8 100644
--- a/railties/test/application/server_test.rb
+++ b/railties/test/application/server_test.rb
@@ -1,12 +1,14 @@
# frozen_string_literal: true
require "isolation/abstract_unit"
+require "console_helpers"
require "rails/command"
require "rails/commands/server/server_command"
module ApplicationTests
class ServerTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
+ include ConsoleHelpers
def setup
build_app
@@ -29,5 +31,31 @@ module ApplicationTests
log = File.read(Rails.application.config.paths["log"].first)
assert_match(/DEPRECATION WARNING: Use `Rails::Application` subclass to start the server is deprecated/, log)
end
+
+ test "restart rails server with custom pid file path" do
+ skip "PTY unavailable" unless available_pty?
+
+ master, slave = PTY.open
+ pid = nil
+
+ begin
+ 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") }
+
+ assert_output("Restarting", master)
+ assert_output("Inherited", master)
+ ensure
+ kill(pid) if pid
+ end
+ end
+
+ private
+ def kill(pid)
+ Process.kill("TERM", pid)
+ Process.wait(pid)
+ rescue Errno::ESRCH
+ end
end
end
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index eb739db9d1..556c2289e7 100644
--- a/railties/test/commands/server_test.rb
+++ b/railties/test/commands/server_test.rb
@@ -190,7 +190,7 @@ class Rails::ServerTest < ActiveSupport::TestCase
ARGV.replace args
options = parse_arguments(args)
- expected = "bin/rails server -p 4567 -b 127.0.0.1 -c dummy_config.ru -d -e test -P tmp/server.pid -C"
+ expected = "bin/rails server -p 4567 -b 127.0.0.1 -c dummy_config.ru -d -e test -P tmp/server.pid -C --restart"
assert_equal expected, options[:restart_cmd]
ensure
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index b50b9364d0..f64ebf5f1f 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -381,31 +381,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_generator_without_skips
- run_generator
- assert_file "config/application.rb", /\s+require\s+["']rails\/all["']/
- assert_file "config/environments/development.rb" do |content|
- assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content)
- end
- assert_file "config/environments/test.rb" do |content|
- assert_match(/config\.action_mailer\.delivery_method = :test/, content)
- end
- assert_file "config/environments/production.rb" do |content|
- assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content)
- assert_match(/^ config\.read_encrypted_secrets = true/, content)
- end
- 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", "--skip-test"]
- assert_file "config/application.rb", /require\s+["']rails["']/
- assert_file "config/application.rb", /require\s+["']active_model\/railtie["']/
- assert_file "config/application.rb", /require\s+["']active_job\/railtie["']/
- assert_file "config/application.rb", /require\s+["']action_controller\/railtie["']/
- assert_file "config/application.rb", /require\s+["']action_view\/railtie["']/
- assert_file "config/application.rb", /require\s+["']active_storage\/engine["']/
- end
-
def test_generator_defaults_to_puma_version
run_generator [destination_root]
assert_gem "puma", "'~> 3.7'"
@@ -419,39 +394,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_generator_if_skip_active_record_is_given
- run_generator [destination_root, "--skip-active-record"]
- assert_no_directory "db/"
- assert_no_file "config/database.yml"
- assert_no_file "app/models/application_record.rb"
- assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
- assert_file "test/test_helper.rb" do |helper_content|
- assert_no_match(/fixtures :all/, helper_content)
- end
- assert_file "bin/setup" do |setup_content|
- assert_no_match(/db:setup/, setup_content)
- end
- assert_file "bin/update" do |update_content|
- assert_no_match(/db:migrate/, update_content)
- end
- end
-
- def test_generator_if_skip_action_mailer_is_given
- run_generator [destination_root, "--skip-action-mailer"]
- assert_file "config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/
- assert_file "config/environments/development.rb" do |content|
- assert_no_match(/config\.action_mailer/, content)
- end
- assert_file "config/environments/test.rb" do |content|
- assert_no_match(/config\.action_mailer/, content)
- end
- assert_file "config/environments/production.rb" do |content|
- assert_no_match(/config\.action_mailer/, content)
- end
- assert_no_directory "app/mailers"
- assert_no_directory "test/mailers"
- end
-
def test_generator_has_assets_gems
run_generator
@@ -459,43 +401,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_gem "uglifier"
end
- def test_generator_if_skip_sprockets_is_given
- run_generator [destination_root, "--skip-sprockets"]
-
- assert_no_file "config/initializers/assets.rb"
-
- assert_file "config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/
-
- assert_file "Gemfile" do |content|
- assert_no_match(/sass-rails/, content)
- assert_no_match(/uglifier/, content)
- assert_no_match(/coffee-rails/, content)
- end
-
- assert_file "config/environments/development.rb" do |content|
- assert_no_match(/config\.assets\.debug/, content)
- end
-
- assert_file "config/environments/production.rb" do |content|
- assert_no_match(/config\.assets\.digest/, content)
- assert_no_match(/config\.assets\.js_compressor/, content)
- assert_no_match(/config\.assets\.css_compressor/, content)
- assert_no_match(/config\.assets\.compile/, content)
- end
- end
-
- def test_generator_if_skip_action_cable_is_given
- run_generator [destination_root, "--skip-action-cable"]
- assert_file "config/application.rb", /#\s+require\s+["']action_cable\/engine["']/
- assert_no_file "config/cable.yml"
- assert_no_file "app/assets/javascripts/cable.js"
- assert_no_directory "app/assets/javascripts/channels"
- assert_no_directory "app/channels"
- assert_file "Gemfile" do |content|
- assert_no_match(/redis/, content)
- end
- end
-
def test_action_cable_redis_gems
run_generator
assert_file "Gemfile", /^# gem 'redis'/
@@ -770,30 +675,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_gitignore_when_sqlite3
- run_generator
-
- assert_file ".gitignore" do |content|
- assert_match(/sqlite3/, content)
- end
- end
-
- def test_gitignore_when_no_active_record
- run_generator [destination_root, "--skip-active-record"]
-
- assert_file ".gitignore" do |content|
- assert_no_match(/sqlite/i, content)
- end
- end
-
- def test_gitignore_when_non_sqlite3_db
- run_generator([destination_root, "-d", "mysql"])
-
- assert_file ".gitignore" do |content|
- assert_no_match(/sqlite/i, content)
- end
- end
-
def test_inclusion_of_ruby_version
run_generator
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index e94b1ac8fe..38130ceb68 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -129,31 +129,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
end
- def test_generating_adds_dummy_app_without_sprockets
- run_generator [destination_root, "--skip-sprockets"]
-
- assert_no_file "test/dummy/config/initializers/assets.rb"
-
- assert_file "test/dummy/config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/
-
- assert_file "Gemfile" do |content|
- assert_no_match(/sass-rails/, content)
- assert_no_match(/uglifier/, content)
- assert_no_match(/coffee-rails/, content)
- end
-
- assert_file "test/dummy/config/environments/development.rb" do |content|
- assert_no_match(/config\.assets\.debug/, content)
- end
-
- assert_file "test/dummy/config/environments/production.rb" do |content|
- assert_no_match(/config\.assets\.digest/, content)
- assert_no_match(/config\.assets\.js_compressor/, content)
- assert_no_match(/config\.assets\.css_compressor/, content)
- assert_no_match(/config\.assets\.compile/, content)
- end
- end
-
def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files
run_generator [destination_root, "-T", "--mountable", "--dummy-path", "my_dummy_app"]
assert_file "Rakefile", /APP_RAKEFILE/
@@ -202,69 +177,13 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
end
- def test_app_generator_without_skips
- run_generator
- assert_file "test/dummy/config/application.rb", /\s+require\s+["']rails\/all["']/
- assert_file "test/dummy/config/environments/development.rb" do |content|
- assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content)
- end
- assert_file "test/dummy/config/environments/test.rb" do |content|
- assert_match(/config\.action_mailer\.delivery_method = :test/, content)
- end
- assert_file "test/dummy/config/environments/production.rb" do |content|
- assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content)
- assert_match(/^ config\.read_encrypted_secrets = true/, content)
- end
- 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 "test/dummy/config/application.rb", /require\s+["']rails["']/
- assert_file "test/dummy/config/application.rb", /require\s+["']active_model\/railtie["']/
- assert_file "test/dummy/config/application.rb", /require\s+["']active_job\/railtie["']/
- assert_file "test/dummy/config/application.rb", /require\s+["']action_controller\/railtie["']/
- assert_file "test/dummy/config/application.rb", /require\s+["']action_view\/railtie["']/
- end
-
- def test_active_record_is_removed_from_frameworks_if_skip_active_record_is_given
- run_generator [destination_root, "--skip-active-record"]
- assert_file "test/dummy/config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
- end
-
def test_ensure_that_skip_active_record_option_is_passed_to_app_generator
run_generator [destination_root, "--skip_active_record"]
- assert_no_file "test/dummy/config/database.yml"
assert_file "test/test_helper.rb" do |contents|
assert_no_match(/ActiveRecord/, contents)
end
end
- def test_action_mailer_is_removed_from_frameworks_if_skip_action_mailer_is_given
- run_generator [destination_root, "--skip-action-mailer"]
- assert_file "test/dummy/config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/
- assert_file "test/dummy/config/environments/development.rb" do |content|
- assert_no_match(/config\.action_mailer/, content)
- end
- assert_file "test/dummy/config/environments/test.rb" do |content|
- assert_no_match(/config\.action_mailer/, content)
- end
- assert_file "test/dummy/config/environments/production.rb" do |content|
- assert_no_match(/config\.action_mailer/, content)
- end
- assert_no_directory "test/dummy/app/mailers"
- end
-
- def test_action_cable_is_removed_from_frameworks_if_skip_action_cable_is_given
- run_generator [destination_root, "--skip-action-cable"]
- assert_file "test/dummy/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/
- assert_no_file "test/dummy/config/cable.yml"
- assert_no_file "test/dummy/app/assets/javascripts/cable.js"
- assert_no_directory "test/dummy/app/channels"
- assert_file "Gemfile" do |content|
- assert_no_match(/redis/, content)
- end
- end
-
def test_ensure_that_database_option_is_passed_to_app_generator
run_generator [destination_root, "--database", "postgresql"]
assert_file "test/dummy/config/database.yml", /postgres/
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 6c0775b50e..56c9b37e1b 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -128,6 +128,120 @@ module SharedGeneratorTests
assert_no_file("app/models/concerns/.keep")
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["']/
+ end
+
+ def test_generator_without_skips
+ run_generator
+ assert_file "#{application_path}/config/application.rb", /\s+require\s+["']rails\/all["']/
+ assert_file "#{application_path}/config/environments/development.rb" do |content|
+ assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content)
+ end
+ assert_file "#{application_path}/config/environments/test.rb" do |content|
+ assert_match(/config\.action_mailer\.delivery_method = :test/, content)
+ end
+ assert_file "#{application_path}/config/environments/production.rb" do |content|
+ assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content)
+ assert_match(/^ config\.read_encrypted_secrets = true/, content)
+ end
+ end
+
+ def test_gitignore_when_sqlite3
+ run_generator
+
+ assert_file ".gitignore" do |content|
+ assert_match(/sqlite3/, content)
+ end
+ end
+
+ def test_gitignore_when_non_sqlite3_db
+ run_generator([destination_root, "-d", "mysql"])
+
+ assert_file ".gitignore" do |content|
+ assert_no_match(/sqlite/i, content)
+ end
+ end
+
+ def test_generator_if_skip_active_record_is_given
+ run_generator [destination_root, "--skip-active-record"]
+ assert_no_directory "#{application_path}/db/"
+ assert_no_file "#{application_path}/config/database.yml"
+ assert_no_file "#{application_path}/app/models/application_record.rb"
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
+ assert_file "test/test_helper.rb" do |helper_content|
+ assert_no_match(/fixtures :all/, helper_content)
+ end
+ assert_file "#{application_path}/bin/setup" do |setup_content|
+ assert_no_match(/db:setup/, setup_content)
+ end
+ assert_file "#{application_path}/bin/update" do |update_content|
+ assert_no_match(/db:migrate/, update_content)
+ end
+ assert_file ".gitignore" do |content|
+ assert_no_match(/sqlite/i, 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["']/
+ assert_file "#{application_path}/config/environments/development.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ assert_file "#{application_path}/config/environments/test.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ assert_file "#{application_path}/config/environments/production.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ assert_no_directory "#{application_path}/app/mailers"
+ assert_no_directory "#{application_path}/test/mailers"
+ end
+
+ def test_generator_if_skip_action_cable_is_given
+ run_generator [destination_root, "--skip-action-cable"]
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/
+ assert_no_file "#{application_path}/config/cable.yml"
+ assert_no_file "#{application_path}/app/assets/javascripts/cable.js"
+ assert_no_directory "#{application_path}/app/assets/javascripts/channels"
+ assert_no_directory "#{application_path}/app/channels"
+ assert_file "Gemfile" do |content|
+ assert_no_match(/redis/, content)
+ end
+ end
+
+ def test_generator_if_skip_sprockets_is_given
+ run_generator [destination_root, "--skip-sprockets"]
+
+ assert_no_file "#{application_path}/config/initializers/assets.rb"
+
+ assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/sass-rails/, content)
+ assert_no_match(/uglifier/, content)
+ assert_no_match(/coffee-rails/, content)
+ end
+
+ assert_file "#{application_path}/config/environments/development.rb" do |content|
+ assert_no_match(/config\.assets\.debug/, content)
+ end
+
+ assert_file "#{application_path}/config/environments/production.rb" do |content|
+ assert_no_match(/config\.assets\.digest/, content)
+ assert_no_match(/config\.assets\.js_compressor/, content)
+ assert_no_match(/config\.assets\.css_compressor/, content)
+ assert_no_match(/config\.assets\.compile/, content)
+ end
+ end
+
def test_generator_for_yarn
run_generator
assert_file "#{application_path}/package.json", /dependencies/