aboutsummaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
...
* | | Remove unused explicit delegation to `klass` in `relation`Ryuta Kamizono2017-09-143-8/+2
| | | | | | | | | | | | | | | | | | | | | It is only used `primary_key` and `connection` in the internal, so it is not needed to delegate others to `klass` explicitly. This doesn't change public behavior because `relation` will delegate missing method to `klass`.
* | | Don't use `collection.table_name` in `collection_cache_key`Ryuta Kamizono2017-09-142-1/+19
| | | | | | | | | | | | | | | Because `collection.table_name` doesn't respect table alias. Use `collection.arel_attribute` instead.
* | | Merge pull request #30598 from yhirano55/update_activejob_basicsRyuta Kamizono2017-09-141-0/+1
|\ \ \ | | | | | | | | Update Active Job Basics [ci skip]
| * | | Update Active Job Basics [ci skip]Yoshiyuki Hirano2017-09-141-0/+1
| | | | | | | | | | | | | | | | * Add Sneakers link to documentation list.
* | | | Don't use `quoted_table_name` in `limited_ids_for`Ryuta Kamizono2017-09-142-2/+8
| | | | | | | | | | | | | | | | | | | | | | | | Because `quoted_table_name` doesn't respect table alias. We should use `arel_attribute` for that, so I added `column_name_from_arel_node` to generate column name from an arel node.
* | | | Merge pull request #30596 from yahonda/address_test_or_with_bind_params_failureRyuta Kamizono2017-09-141-1/+1
|\ \ \ \ | | | | | | | | | | Address random `test_or_with_bind_params` failures
| * | | | Address random `test_or_with_bind_params` failuresYasuo Honda2017-09-131-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Reported at https://travis-ci.org/rails/rails/jobs/274370258 - `Post.find([1, 2])` generates this query below: ```sql SELECT "posts".* FROM "posts" WHERE "posts"."id" IN ($1, $2) [["id", 1], ["id", 2]] ``` - `Post.where(id: 1).or(Post.where(id: 2)).to_a` generates this query below: ```sql SELECT "posts".* FROM "posts" WHERE ("posts"."id" = $1 OR "posts"."id" = $2) [["id", 1], ["id", 2]] ``` Most of the time these two queries return the same result but the order of records are not guaranteed from SQL point of view then added `sort` before comparing them.
* | | | | `quoted_table_name` doesn't respect table aliasRyuta Kamizono2017-09-142-1/+12
|/ / / / | | | | | | | | | | | | So using `arel_attribute(primary_key).asc` in `batch_order` instead.
* | | | Make `in_batches` queries to preparableRyuta Kamizono2017-09-141-3/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Before: ```sql SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 2 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 4 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 6 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 8 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > 10 ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] ``` After: ```sql SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT ? [["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 4], ["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 6], ["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 8], ["LIMIT", 2]] SELECT "posts".* FROM "posts" WHERE "posts"."id" > ? ORDER BY "posts"."id" ASC LIMIT ? [["id", 10], ["LIMIT", 2]] ```
* | | | [ci skip] Prefer credentials to secrets in docs.Kasper Timm Hansen2017-09-138-80/+45
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Removes most mentions of secrets.secret_key_base and explains credentials instead. Also removes some very stale upgrade notices about Rails 3/4.
* | | | Merge pull request #30591 from yhirano55/update_getting_startedVipul A M2017-09-131-0/+2
|\ \ \ \ | |/ / / |/| | | Update Getting Started with Rails [ci skip]
| * | | Update Getting Started with Rails [ci skip]Yoshiyuki Hirano2017-09-141-0/+2
|/ / /
* | | Merge pull request #30568 from starknx/fix-active-storage-firefoxJavan Makhmali2017-09-133-1/+3
|\ \ \ | | | | | | | | Fix Active Storage direct uploads in Firefox
| * | | Fix activestorage direct upload on firefox_N_I_X_O_N_2017-09-123-1/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Direct upload on firefox is not working because it can’t handle click events on “input[type=submit]”. After a successful upload we can see on console a xml parse error, it happens because AWS S3 success body response is empty, so changing the reponseType suppress this error.
* | | | Merge pull request #30587 from ydakuka/fix-created-atप्रथमेश Sonpatki2017-09-131-1/+1
|\ \ \ \ | |/ / / |/| | | Fix created_at [ci skip]
| * | | Fix created_at [ci skip]Yauheni Dakuka2017-09-131-1/+1
|/ / /
* | | Use escaped character in regexpyhirano552017-09-111-1/+1
| | |
* | | Rename activestorage/test/service/configurations.yml to ↵George Claghorn2017-09-111-0/+0
| | | | | | | | | | | | activestorage/test/service/configurations.example.yml
* | | Ignore activestorage/test/service/configurations.ymlGeorge Claghorn2017-09-111-0/+1
|/ / | | | | | | This file is intended to be modified as needed for testing services locally. Changes to it should rarely, if ever, be committed.
* | Add credentials using a generic EncryptedConfiguration class (#30067)David Heinemeier Hansson2017-09-1128-123/+678
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * WIP: Add credentials using a generic EncryptedConfiguration class This is sketch code so far. * Flesh out EncryptedConfiguration and test it * Better name * Add command and generator for credentials * Use the Pathnames * Extract EncryptedFile from EncryptedConfiguration and add serializers * Test EncryptedFile * Extract serializer validation * Stress the point about losing comments * Allow encrypted configuration to be read without parsing for display * Use credentials by default and base them on the master key * Derive secret_key_base in test/dev, source it from credentials in other envs And document the usage. * Document the new credentials setup * Stop generating the secrets.yml file now that we have credentials * Document what we should have instead Still need to make it happen, tho. * [ci skip] Keep wording to `key base`; prefer defaults. Usually we say we change defaults, not "spec" out a release. Can't use backticks in our sdoc generated documentation either. * Abstract away OpenSSL; prefer MessageEncryptor. * Spare needless new when raising. * Encrypted file test shouldn't depend on subclass. * [ci skip] Some woordings. * Ditch serializer future coding. * I said flip it. Flip it good. * [ci skip] Move require_master_key to the real production.rb. * Add require_master_key to abort the boot process. In case the master key is required in a certain environment we should inspect that the key is there and abort if it isn't. * Print missing key message and exit immediately. Spares us a lengthy backtrace and prevents further execution. I've verified the behavior in a test app, but couldn't figure the test out as loading the app just exits immediately with: ``` /Users/kasperhansen/Documents/code/rails/activesupport/lib/active_support/testing/isolation.rb:23:in `load': marshal data too short (ArgumentError) from /Users/kasperhansen/Documents/code/rails/activesupport/lib/active_support/testing/isolation.rb:23:in `run' from /Users/kasperhansen/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/minitest-5.10.2/lib/minitest.rb:830:in `run_one_method' from /Users/kasperhansen/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/minitest-5.10.2/lib/minitest/parallel.rb:32:in `block (2 levels) in start' ``` It's likely we need to capture and prevent the exit somehow. Kernel.stub(:exit) didn't work. Leaving it for tomorrow. * Fix require_master_key config test. Loading the app would trigger the `exit 1` per require_master_key's semantics, which then aborted the test. Fork and wait for the child process to finish, then inspect the exit status. Also check we aborted because of a missing master key, so something else didn't just abort the boot. Much <3 to @tenderlove for the tip. * Support reading/writing configs via methods. * Skip needless deep symbolizing. * Remove save; test config reader elsewhere. * Move secret_key_base check to when we're reading it. Otherwise we'll abort too soon since we don't assign the secret_key_base to secrets anymore. * Add missing string literal comments; require unneeded yaml require. * ya ya ya, rubocop. * Add master_key/credentials after bundle. Then we can reuse the existing message on `rails new bc4`. It'll look like: ``` Using web-console 3.5.1 from https://github.com/rails/web-console.git (at master@ce985eb) Using rails 5.2.0.alpha from source at `/Users/kasperhansen/Documents/code/rails` Using sass-rails 5.0.6 Bundle complete! 16 Gemfile dependencies, 72 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed. Adding config/master.key to store the master encryption key: 97070158c44b4675b876373a6bc9d5a0 Save this in a password manager your team can access. If you lose the key, no one, including you, can access anything encrypted with it. create config/master.key ``` And that'll be executed even if `--skip-bundle` was passed. * Ensure test app has secret_key_base. * Assign secret_key_base to app or omit. * Merge noise * Split options for dynamic delegation into its own method and use deep symbols to make it work * Update error to point to credentials instead * Appease Rubocop * Validate secret_key_base when reading it. Instead of relying on the validation in key_generator move that into secret_key_base itself. * Fix generator and secrets test. Manually add config.read_encrypted_secrets since it's not there by default anymore. Move mentions of config/secrets.yml to config/credentials.yml.enc. * Remove files I have no idea how they got here. * [ci skip] swap secrets for credentials. * [ci skip] And now, changelogs are coming.
* | Merge pull request #30564 from tgxworld/fix_incorrect_memoizationRyuta Kamizono2017-09-111-1/+5
|\ \ | | | | | | PERF: Incorrect memoization in `ActiveRecord::Associations::Preloader…
| * | PERF: Incorrect memoization in ↵Guo Xiang Tan2017-09-111-1/+5
|/ / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `ActiveRecord::Associations::Preloader::Association`. ``` require 'active_record' require 'benchmark/ips' ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL')) ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name, :email t.integer :topic_id t.timestamps null: false end create_table :topics, force: true do |t| t.string :title t.timestamps null: false end end attributes = { name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', email: 'foobar@email.com' } class Topic < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :topic end 100.times do User.create!(attributes) end users = User.first(50) 100.times do Topic.create!(title: 'This is a topic', users: users) end Benchmark.ips do |x| x.config(time: 10, warmup: 5) x.report("preload") do User.includes(:topic).all.to_a end end ``` ``` Calculating ------------------------------------- preload 25.000 i/100ms ------------------------------------------------- preload 251.772 (± 1.2%) i/s - 2.525k ``` ``` Calculating ------------------------------------- preload 26.000 i/100ms ------------------------------------------------- preload 270.392 (± 1.1%) i/s - 2.704k ```
* | Merge pull request #30563 from y-yagi/remove_not_used_gems_by_defaultRyuta Kamizono2017-09-111-2/+0
|\ \ | | | | | | Remove not used gems by default [ci skip]
| * | Remove not used gems by default [ci skip]yuuji.yaginuma2017-09-111-2/+0
|/ / | | | | | | | | | | | | | | | | `rack-mount` switched to journey with 5f0b37c. Also, `rack-cache` will not be used unless you explicitly specify it. Ref: https://github.com/rails/rails/commit/037e50ec39c7c3d58a102a6584e2740652420d1d https://github.com/rails/rails/commit/1fc795468525d8622cdca474a54c8310a514aa46
* | Use v3 of the AWS SDKYuji Yaginuma2017-09-093-10/+15
| |
* | Merge pull request #30552 from y-yagi/remove_needless_silence_warningsRyuta Kamizono2017-09-093-17/+3
|\ \ | | | | | | Remove needless `silence_warnings`
| * | Remove needless `silence_warnings`yuuji.yaginuma2017-09-093-17/+3
|/ / | | | | | | Since ff30db1, warning is not shown.
* | Merge pull request #30536 from y-yagi/reorganize_secrets_testMatthew Draper2017-09-082-18/+20
|\ \ | | | | | | Reorganize secrets test to use only `isolation/abstract_unit`
| * | Remove unused `fork` arg for `rails`yuuji.yaginuma2017-09-081-4/+2
| | |
| * | Reorganize secrets test to use only `isolation/abstract_unit`yuuji.yaginuma2017-09-071-14/+18
| | | | | | | | | | | | | | | | | | | | | | | | Currently, secrets test uses `abstract_unit` and `isolation/abstract_unit`. This is a bit odd. Therefore, reorganize it so that use only `isolation/abstract_unit`. Context: https://github.com/rails/rails/pull/30520#issuecomment-327409586
* | | Merge pull request #30540 from y-yagi/do_not_install_unused_gemRyuta Kamizono2017-09-082-9/+1
|\ \ \ | | | | | | | | Do not install unused gem
| * | | Do not install unused gemyuuji.yaginuma2017-09-082-9/+1
| |/ / | | | | | | | | | | | | | | | `qu-redis` is need for qu adapter test. However, since 8ecc5ab, qu adapter test has not been executed, it is unnecessary now.
* | | Merge pull request #30524 from tgxworld/recover_plucK_performanceSean Griffin2017-09-071-14/+17
|\ \ \ | | | | | | | | PERF: Recover `ActiveRecord::pluck` performance.
| * | | PERF: Recover `ActiveRecord::pluck` performance.Guo Xiang Tan2017-09-061-14/+17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ```ruby require 'active_record' require 'benchmark/ips' ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL')) ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name, :email t.timestamps null: false end end attributes = { name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', email: 'foobar@email.com' } class User < ActiveRecord::Base; end 1000.times do User.create!(attributes) end Benchmark.ips do |x| x.config(time: 10, warmup: 2) x.report('pluck 1 column') do User.pluck(:id) end x.report('pluck 2 columns') do User.pluck(:id, :email) end x.report('pluck 1 column with scope') do User.where(id: 1000).pluck(:id) end x.report('pluck 2 columns with scope') do User.where(id: 1000).pluck(:id, :email) end end ``` ``` Calculating ------------------------------------- pluck 1 column 122.000 i/100ms pluck 2 columns 74.000 i/100ms pluck 1 column with scope 615.000 i/100ms pluck 2 columns with scope 515.000 i/100ms ------------------------------------------------- pluck 1 column 1.272k (± 3.9%) i/s - 12.810k pluck 2 columns 750.096 (± 3.3%) i/s - 7.548k pluck 1 column with scope 6.074k (± 4.1%) i/s - 60.885k pluck 2 columns with scope 5.158k (± 2.7%) i/s - 52.015k ``` ``` Calculating ------------------------------------- pluck 1 column 126.000 i/100ms pluck 2 columns 78.000 i/100ms pluck 1 column with scope 457.000 i/100ms pluck 2 columns with scope 434.000 i/100ms ------------------------------------------------- pluck 1 column 1.266k (± 2.1%) i/s - 12.726k pluck 2 columns 795.061 (± 3.0%) i/s - 7.956k pluck 1 column with scope 4.660k (± 2.1%) i/s - 46.614k pluck 2 columns with scope 4.355k (± 2.3%) i/s - 43.834k ``` ``` Calculating ------------------------------------- pluck 1 column 126.000 i/100ms pluck 2 columns 78.000 i/100ms pluck 1 column with scope 539.000 i/100ms pluck 2 columns with scope 481.000 i/100ms ------------------------------------------------- pluck 1 column 1.308k (± 3.4%) i/s - 13.104k pluck 2 columns 798.604 (± 2.8%) i/s - 8.034k pluck 1 column with scope 5.530k (± 3.4%) i/s - 55.517k pluck 2 columns with scope 4.914k (± 2.7%) i/s - 49.543k ``` ``` Calculating ------------------------------------- pluck 1 column 139.000 i/100ms pluck 2 columns 79.000 i/100ms pluck 1 column with scope 580.000 i/100ms pluck 2 columns with scope 526.000 i/100ms ------------------------------------------------- pluck 1 column 1.337k (± 3.0%) i/s - 13.483k pluck 2 columns 806.776 (± 2.7%) i/s - 8.137k pluck 1 column with scope 5.924k (± 4.1%) i/s - 59.160k pluck 2 columns with scope 5.276k (± 3.1%) i/s - 53.126k ```
* | | | Remove unnecessary `join_type` in `AssociationScope`Ryuta Kamizono2017-09-081-5/+1
| | | | | | | | | | | | | | | | | | | | | | | | This method was moved from `JoinHelper` in 0fddc3c1, but it is only used for `table.create_join` in the internal and `Nodes::InnerJoin` is default join klass. So it is not needed to pass it explicitly.
* | | | Remove duplicated `klass` method in `AssociationReflection`Ryuta Kamizono2017-09-081-16/+11
| | | | | | | | | | | | | | | | | | | | The superclass (`MacroReflection`) already have the same method definition.
* | | | Remove unused `primary_key_type` and `quoted_table_name` in `Reflection`Ryuta Kamizono2017-09-081-8/+0
| | | | | | | | | | | | | | | | | | | | `primary_key_type` is no longer used since #26718. `quoted_table_name` is no longer used since Rails 3.1.
* | | | Add an extra assertion to ensure dumping schema default as expectedRyuta Kamizono2017-09-081-1/+4
| | | |
* | | | Fix `quote_default_expression` for UUID with array defaultRyuta Kamizono2017-09-082-1/+11
| | | | | | | | | | | | | | | | Fixes #30539.
* | | | Merge pull request #30538 from koic/ci_against_jruby_9_1_13_0Guillermo Iguaran2017-09-061-3/+3
|\ \ \ \ | | | | | | | | | | CI against JRuby 9.1.13.0
| * | | | CI against JRuby 9.1.13.0Koichi ITO2017-09-071-3/+3
|/ / / / | | | | | | | | | | | | http://jruby.org/2017/09/06/jruby-9-1-13-0.html
* | | | Don't pass `table` to `last_chain_scope` and `next_chain_scope`Ryuta Kamizono2017-09-072-19/+17
| | | | | | | | | | | | | | | | | | | | | | | | Because `table` is part of `reflection`, don't need to pass it explicitly. And also, naming `alias_name` to `table` is a little confusing. `aliased_table` is preferable than `alias_name`.
* | | | `RuntimeReflection` is not a subclass of `PolymorphicReflection`Ryuta Kamizono2017-09-071-26/+6
| | | | | | | | | | | | | | | | | | | | | | | | `PolymorphicReflection` is an internal class that is used in `ThroughReflection`. But `RuntimeReflection` is used for the head of chain in `AssociationScope`. These are totally different things.
* | | | `has_many :through` with unscope should affect to through scopeRyuta Kamizono2017-09-074-23/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The order of scope evaluation should be from through scope to the association's own scope. Otherwise the association's scope cannot affect to through scope. Fixes #13677. Closes #28449.
* | | | :scissors:Ryuta Kamizono2017-09-071-1/+1
| | | | | | | | | | | | | | | | [ci skip]
* | | | Merge pull request #30533 from ydakuka/fix-typo-fully-qualifiedVipul A M2017-09-062-4/+4
|\ \ \ \ | |_|/ / |/| | | fix type fully qualified [ci skip]
| * | | fix type fully qualified [ci skip]Yauheni Dakuka2017-09-062-4/+4
|/ / /
* | | Merge pull request #30367 from ptoomey3/consistent-cache-control-headersAaron Patterson2017-09-053-8/+34
|\ \ \ | | | | | | | | Normalize/process Cache-Control headers consistently
| * | | Don't touch an unused headerPatrick Toomey2017-08-231-1/+0
| | | | | | | | | | | | | | | | | | | | | | | | The prior logic explictly set `Cache-Control` to `nil`. But, we would only reach that logic if the header was not set to begin with. So, rather than give it any value at all, just leave it alone.
| * | | Decouple the merge/normalization and conditional cache control logicPatrick Toomey2017-08-232-5/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The prior logic was trying to do too many things at once. For all responses, we want to perform two distinct steps: * Merge/normalize the `Cache-Control` values found in HTTP headers and those found in the `@cache_control` hash. * Conditionally set a default `Cache-Control` header value when we have an ETag This change separates these concerns since the merge/normalize step should occur for all responses, but the second should only occur when we have already set an ETag/last modified value. Normally ETag middleware will set a default `Cache-Control`, but only if an existing ETag is not already set. So, in the cases where an ETag is set, we need to set the default `Cache-Control` value ourselves.