diff options
16 files changed, 195 insertions, 127 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 4feb8e2968..84ab3a01ca 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -141,7 +141,7 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.0.4) + concurrent-ruby (1.0.5) connection_pool (2.2.1) cookiejar (0.3.3) curses (1.0.2) @@ -187,8 +187,8 @@ GEM ffi (1.9.17) ffi (1.9.17-x64-mingw32) ffi (1.9.17-x86-mingw32) - globalid (0.3.7) - activesupport (>= 4.1.0) + globalid (0.4.0) + activesupport (>= 4.2.0) hiredis (0.6.1) http_parser.rb (0.6.0) i18n (0.8.1) @@ -341,12 +341,12 @@ GEM rack (>= 1, < 3) thor (0.19.4) thread (0.1.7) - thread_safe (0.3.5) + thread_safe (0.3.6) tilt (2.0.5) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.0) - tzinfo (1.2.2) + tzinfo (1.2.3) thread_safe (~> 0.1) tzinfo-data (1.2016.10) tzinfo (>= 1.0.0) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ffe588b3c5..f74425b281 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Support Descending Indexes for MySQL. + + MySQL 8.0.1 and higher supports descending indexes: `DESC` in an index definition is no longer ignored. + See https://dev.mysql.com/doc/refman/8.0/en/descending-indexes.html. + + *Ryuta Kamizono* + * Fix inconsistency with changed attributes when overriding AR attribute reader. *bogdanvlviv* diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 4682afc188..46d7f84efd 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -2,8 +2,33 @@ module ActiveRecord module ConnectionAdapters #:nodoc: # Abstract representation of an index definition on a table. Instances of # this type are typically created and returned by methods in database - # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes - IndexDefinition = Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment) #:nodoc: + # adapters. e.g. ActiveRecord::ConnectionAdapters::MySQL::SchemaStatements#indexes + class IndexDefinition # :nodoc: + attr_reader :table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment + + def initialize( + table, name, + unique = false, + columns = [], + lengths: {}, + orders: {}, + where: nil, + type: nil, + using: nil, + comment: nil + ) + @table = table + @name = name + @unique = unique + @columns = columns + @lengths = lengths + @orders = orders + @where = where + @type = type + @using = using + @comment = comment + end + end # Abstract representation of a column definition. Instances of this type # are typically created by methods in TableDefinition, and added to the 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 f118e086bb..71fa7b929c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -48,9 +48,6 @@ module ActiveRecord json: { name: "json" }, } - INDEX_TYPES = [:fulltext, :spatial] - INDEX_USINGS = [:btree, :hash] - class StatementPool < ConnectionAdapters::StatementPool private def dealloc(stmt) stmt[:stmt].close @@ -93,10 +90,8 @@ module ActiveRecord true end - # Technically MySQL allows to create indexes with the sort order syntax - # but at the moment (5.5) it doesn't yet implement them def supports_index_sort_order? - true + !mariadb? && version >= "8.0.1" end def supports_transaction_isolation? @@ -304,36 +299,6 @@ module ActiveRecord execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name end - # Returns an array of indexes for the given table. - def indexes(table_name, name = nil) #:nodoc: - if name - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing name to #indexes is deprecated without replacement. - MSG - end - - indexes = [] - current_index = nil - execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result| - each_hash(result) do |row| - if current_index != row[:Key_name] - next if row[:Key_name] == "PRIMARY" # skip the primary key - current_index = row[:Key_name] - - mysql_index_type = row[:Index_type].downcase.to_sym - index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil - index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil - indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], {}, nil, nil, index_type, index_using, row[:Index_comment].presence) - end - - indexes.last.columns << row[:Column_name] - indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part] - end - end - - indexes - end - def table_comment(table_name) # :nodoc: scope = quoted_scope(table_name) 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 9e2d0fb5e7..571edffec7 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb @@ -2,6 +2,49 @@ module ActiveRecord module ConnectionAdapters module MySQL module SchemaStatements # :nodoc: + # Returns an array of indexes for the given table. + def indexes(table_name, name = nil) + if name + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing name to #indexes is deprecated without replacement. + MSG + end + + indexes = [] + current_index = nil + execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result| + each_hash(result) do |row| + if current_index != row[:Key_name] + next if row[:Key_name] == "PRIMARY" # skip the primary key + current_index = row[:Key_name] + + mysql_index_type = row[:Index_type].downcase.to_sym + case mysql_index_type + when :fulltext, :spatial + index_type = mysql_index_type + when :btree, :hash + index_using = mysql_index_type + end + + indexes << IndexDefinition.new( + row[:Table], + row[:Key_name], + row[:Non_unique].to_i == 0, + type: index_type, + using: index_using, + comment: row[:Index_comment].presence + ) + end + + indexes.last.columns << row[:Column_name] + indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part] + indexes.last.orders.merge!(row[:Column_name] => :desc) if row[:Collation] == "D" + end + end + + indexes + end + private def schema_creation MySQL::SchemaCreation.new(self) 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 02a6da2f71..5b483ad4ab 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -140,8 +140,17 @@ module ActiveRecord ] end - IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment.presence) - end.compact + IndexDefinition.new( + table_name, + index_name, + unique, + columns, + orders: orders, + where: where, + using: using.to_sym, + comment: comment.presence + ) + end end def table_options(table_name) # :nodoc: 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 8066a05c5e..e02491edb6 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb @@ -2,6 +2,41 @@ module ActiveRecord module ConnectionAdapters module SQLite3 module SchemaStatements # :nodoc: + # Returns an array of indexes for the given table. + def indexes(table_name, name = nil) + if name + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing name to #indexes is deprecated without replacement. + MSG + end + + exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row| + index_sql = select_value(<<-SQL, "SCHEMA") + SELECT sql + FROM sqlite_master + WHERE name = #{quote(row['name'])} AND type = 'index' + UNION ALL + SELECT sql + FROM sqlite_temp_master + WHERE name = #{quote(row['name'])} AND type = 'index' + SQL + + /\sWHERE\s+(?<where>.+)$/i =~ index_sql + + columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col| + col["name"] + end + + IndexDefinition.new( + table_name, + row["name"], + row["unique"] != 0, + columns, + where: where + ) + end + 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 c54b88f7d1..e2c05ccc4e 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -259,37 +259,6 @@ module ActiveRecord # SCHEMA STATEMENTS ======================================== - # Returns an array of indexes for the given table. - def indexes(table_name, name = nil) #:nodoc: - if name - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing name to #indexes is deprecated without replacement. - MSG - end - - exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row| - sql = <<-SQL - SELECT sql - FROM sqlite_master - WHERE name=#{quote(row['name'])} AND type='index' - UNION ALL - SELECT sql - FROM sqlite_temp_master - WHERE name=#{quote(row['name'])} AND type='index' - SQL - index_sql = exec_query(sql).first["sql"] - match = /\sWHERE\s+(.+)$/i.match(index_sql) - where = match[1] if match - IndexDefinition.new( - table_name, - row["name"], - row["unique"] != 0, - exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col| - col["name"] - }, nil, nil, where) - end - end - def primary_keys(table_name) # :nodoc: pks = table_structure(table_name).select { |f| f["pk"] > 0 } pks.sort_by { |f| f["pk"] }.map { |f| f["name"] } diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index fccba4738f..cb8d449ba9 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -182,7 +182,11 @@ class SchemaDumperTest < ActiveRecord::TestCase if current_adapter?(:PostgreSQLAdapter) assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition elsif current_adapter?(:Mysql2Adapter) - assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }', index_definition + if ActiveRecord::Base.connection.supports_index_sort_order? + assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }, order: { rating: :desc }', index_definition + else + assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }', index_definition + end else assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition end diff --git a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb index 16a6789f8d..88a34128c9 100644 --- a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb +++ b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb @@ -1,7 +1,7 @@ class Array # The human way of thinking about adding stuff to the end of a list is with append. - alias_method :append, :<< + alias_method :append, :push unless [].respond_to?(:append) # The human way of thinking about adding stuff to the beginning of a list is with prepend. - alias_method :prepend, :unshift + alias_method :prepend, :unshift unless [].respond_to?(:prepend) end diff --git a/guides/source/5_1_release_notes.md b/guides/source/5_1_release_notes.md index cf6158b6fa..43b5a0fdaf 100644 --- a/guides/source/5_1_release_notes.md +++ b/guides/source/5_1_release_notes.md @@ -228,60 +228,62 @@ can generate form tags based on URLs, scopes or models. Railties -------- +Please refer to the [Changelog][railties] for detailed changes. + ### Removals ### Deprecations -### Notable Changes - -Please refer to the [Changelog][railties] for detailed changes. +### Notable changes Action Cable ----------- +Please refer to the [Changelog][action-cable] for detailed changes. + ### Removals ### Deprecations -### Notable Changes +### Notable changes * Added support for `channel_prefix` to Redis and evented Redis adapters in `cable.yml` to avoid name collisions when using the same Redis server with multiple applications. ([Pull Request](https://github.com/rails/rails/pull/27425)) -Please refer to the [Changelog][action-cable] for detailed changes. - Action Pack ----------- +Please refer to the [Changelog][action-pack] for detailed changes. + ### Removals ### Deprecations -### Notable Changes - -Please refer to the [Changelog][action-pack] for detailed changes. +### Notable changes Action View ------------- +Please refer to the [Changelog][action-view] for detailed changes. + ### Removals ### Deprecations -### Notable Changes - -Please refer to the [Changelog][action-view] for detailed changes. +### Notable changes Action Mailer ------------- +Please refer to the [Changelog][action-mailer] for detailed changes. + ### Removals ### Deprecations -### Notable Changes +### Notable changes * Allowed setting custom content type when attachments are included and body is set inline. @@ -298,16 +300,16 @@ Action Mailer an `args` key. ([Pull Request](https://github.com/rails/rails/pull/27900)) -Please refer to the [Changelog][action-mailer] for detailed changes. - Active Record ------------- +Please refer to the [Changelog][active-record] for detailed changes. + ### Removals ### Deprecations -### Notable Changes +### Notable changes * Skipped comments in the output of `mysqldump` command by default. ([Pull Request](https://github.com/rails/rails/pull/23301)) @@ -320,38 +322,38 @@ Active Record * Pass `"-v ON_ERROR_STOP=1"` flag with `psql` command to not suppress SQL errors. ([Pull Request](https://github.com/rails/rails/pull/24773)) -Please refer to the [Changelog][active-record] for detailed changes. - Active Model ------------ +Please refer to the [Changelog][active-model] for detailed changes. + ### Removals ### Deprecations -### Notable Changes - -Please refer to the [Changelog][active-model] for detailed changes. +### Notable changes Active Job ----------- +Please refer to the [Changelog][active-job] for detailed changes. + ### Removals ### Deprecations -### Notable Changes - -Please refer to the [Changelog][active-job] for detailed changes. +### Notable changes Active Support -------------- +Please refer to the [Changelog][active-support] for detailed changes. + ### Removals ### Deprecations -### Notable Changes +### Notable changes * Added `Module#delegate_missing_to` to delegate method calls not defined for the current object to a proxy object. @@ -361,8 +363,6 @@ Active Support of the current date & time. ([Pull Request](https://github.com/rails/rails/pull/24930)) -Please refer to the [Changelog][active-support] for detailed changes. - Credits ------- diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index cbaf9100f7..2a6a87c232 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -338,7 +338,7 @@ this: end ``` -Notice the format.js in the `respond_to` block; that allows the controller to +Notice the `format.js` in the `respond_to` block: that allows the controller to respond to your Ajax request. You then have a corresponding `app/views/users/create.js.erb` view file that generates the actual JavaScript code that will be sent and executed on the client side. @@ -355,7 +355,7 @@ which uses Ajax to speed up page rendering in most applications. ### How Turbolinks Works -Turbolinks attaches a click handler to all `<a>` on the page. If your browser +Turbolinks attaches a click handler to all `<a>` tags on the page. If your browser supports [PushState](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history#The_pushState%28%29_method), Turbolinks will make an Ajax request for the page, parse the response, and @@ -385,7 +385,7 @@ $(document).ready -> ``` However, because Turbolinks overrides the normal page loading process, the -event that this relies on will not be fired. If you have code that looks like +event that this relies upon will not be fired. If you have code that looks like this, you must change your code to do this instead: ```coffeescript diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 89f7b5991f..f8a923141d 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -386,7 +386,9 @@ module Rails def secrets @secrets ||= begin secrets = ActiveSupport::OrderedOptions.new - secrets.merge! Rails::Secrets.parse(config.paths["config/secrets"].existent, env: Rails.env) + files = config.paths["config/secrets"].existent + files = files.reject { |path| path.end_with?(".enc") } unless config.read_encrypted_secrets + secrets.merge! Rails::Secrets.parse(files, env: Rails.env) # Fallback to config.secret_key_base if secrets.secret_key_base isn't set secrets.secret_key_base ||= config.secret_key_base diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index 4223c38146..dc0491035d 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -81,7 +81,6 @@ INFO initializer :set_secrets_root, group: :all do Rails::Secrets.root = root - Rails::Secrets.read_encrypted_secrets = config.read_encrypted_secrets end end end diff --git a/railties/lib/rails/secrets.rb b/railties/lib/rails/secrets.rb index 2a95712cd9..8b644f212c 100644 --- a/railties/lib/rails/secrets.rb +++ b/railties/lib/rails/secrets.rb @@ -14,12 +14,10 @@ module Rails end @cipher = "aes-128-gcm" - @read_encrypted_secrets = false @root = File # Wonky, but ensures `join` uses the current directory. class << self - attr_writer :root - attr_accessor :read_encrypted_secrets + attr_writer :root def parse(paths, env:) paths.each_with_object(Hash.new) do |path, all_secrets| @@ -88,11 +86,7 @@ module Rails def preprocess(path) if path.end_with?(".enc") - if @read_encrypted_secrets - decrypt(IO.binread(path)) - else - "" - end + decrypt(IO.binread(path)) else IO.read(path) end diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb index 953408f0b4..36c8ef1fd9 100644 --- a/railties/test/secrets_test.rb +++ b/railties/test/secrets_test.rb @@ -9,22 +9,22 @@ class Rails::SecretsTest < ActiveSupport::TestCase def setup build_app - - @old_read_encrypted_secrets, Rails::Secrets.read_encrypted_secrets = - Rails::Secrets.read_encrypted_secrets, true end def teardown - Rails::Secrets.read_encrypted_secrets = @old_read_encrypted_secrets - teardown_app end test "setting read to false skips parsing" do - Rails::Secrets.read_encrypted_secrets = false + run_secrets_generator do + Rails::Secrets.write(<<-end_of_secrets) + test: + yeah_yeah: lets-walk-in-the-cool-evening-light + end_of_secrets - Dir.chdir(app_path) do - assert_equal Hash.new, Rails::Secrets.parse(%w( config/secrets.yml.enc ), env: "production") + Rails.application.config.read_encrypted_secrets = false + Rails.application.instance_variable_set(:@secrets, nil) # Dance around caching 💃🕺 + assert_not Rails.application.secrets.yeah_yeah end end @@ -90,11 +90,27 @@ class Rails::SecretsTest < ActiveSupport::TestCase end_of_secrets Rails.application.config.root = app_path + Rails.application.config.read_encrypted_secrets = true Rails.application.instance_variable_set(:@secrets, nil) # Dance around caching 💃🕺 assert_equal "lets-walk-in-the-cool-evening-light", Rails.application.secrets.yeah_yeah end end + test "refer secrets inside env config" do + run_secrets_generator do + Rails::Secrets.write(<<-end_of_yaml) + production: + some_secret: yeah yeah + end_of_yaml + + add_to_env_config "production", <<-end_of_config + config.dereferenced_secret = Rails.application.secrets.some_secret + end_of_config + + assert_equal "yeah yeah\n", `bin/rails runner -e production "puts Rails.application.config.dereferenced_secret"` + end + end + private def run_secrets_generator Dir.chdir(app_path) do |