Commit Graph

7686 Commits

Author SHA1 Message Date
Jonathan Hefner
c0a5929f3a Fix serialization of non-ASCII-only bare strings
To use a binary-encoded string as a byte buffer, appended strings should
be force-encoded as binary.  Otherwise, appending a non-ASCII-only
string will raise `Encoding::CompatibilityError`.

Fixes #48748.
2023-07-17 13:02:32 -05:00
Guillermo Iguaran
66676ce499
Merge pull request #48680 from p8/activesupport/message-verifier-inspect
Don't show secrets for `MessageVerifier#inspect` and `KeyGenerator#inspect`
2023-07-10 10:56:18 -07:00
Petrik de Heus
2e597fa423
Merge pull request #48661 from p8/activesupport/document-cache-multiple-return-value
Document return value of `Rails.cache.delete_multi`
2023-07-06 21:58:42 +02:00
Petrik
5117da2b65 Don't show secrets for MessageVerifier#inspect and KeyGenerator#inspect
Before:

```ruby
ActiveSupport::MessageVerifier.new(secret).inspect
"#<ActiveSupport::MessageVerifier:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
ActiveSupport::KeyGenerator.new(secret).inspect
"#<ActiveSupport::KeyGenerator:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
```

After:

```ruby
ActiveSupport::MessageVerifier::Aes256Gcm(secret).inspect
"#<ActiveSupport::MessageVerifier:0x0000000104888038>"
ActiveSupport::KeyGenerator::Aes256Gcm(secret).inspect
"#<ActiveSupport::KeyGenerator:0x0000000104888038>"
```
2023-07-06 21:51:22 +02:00
Eileen M. Uchitelle
1cbd88f918
Merge pull request #48662 from skipkayhil/hm-fix-memcache-6-1-deprecation
Fix MemCacheStore not warning on 6.1 cache format
2023-07-06 09:10:51 -04:00
Jonathan del Strother
9e31b19444
Remove unused RedisCacheStore#redis_options
The underlying ivar was removed in c07812cee2be727b334ed2c89ef1ceaa6b467447
2023-07-06 12:59:01 +01:00
Jean Boussier
55a852a63f Make Active Support Cache treat deserialization errors like cache misses
This help treating caches entries as expandable.

Because Marshal will hapily serialize almost everything, It's not uncommon to
inadvertently cache a class that's not particularly stable, and cause deserialization
errors on rollout when the implementation changes.

E.g. https://github.com/sporkmonger/addressable/pull/508

With this change, in case of such event, the hit rate will suffer for a
bit, but the application won't return 500s responses.
2023-07-06 10:51:27 +02:00
Jonathan Hefner
67dd6ee325 Refactor RedisCacheStore options handling
This makes it easier to add new base options with default values to
`Cache::Store`.
2023-07-05 17:11:26 -05:00
Jonathan Hefner
c7a23018f5 Extract Cache::Entry into a separate file
`activesupport/lib/active_support/cache.rb` is already rather long, and
having a separate file allows subclasses of `Cache::Entry` to be defined
without first requiring `activesupport/lib/active_support/cache.rb`.
2023-07-05 17:11:26 -05:00
Jonathan Hefner
ef703fecac Replace NullCoder with :passthrough coder
`NullCoder` is functionally equivalent to the `:passthrough` coder.
Additionally, `NullCoder` was added in c4845aa7791839fcdf723dc77e3df258e7274496
to serve as the default coder for `MemCacheStore`, but `MemCacheStore`
now uses `:passthrough` as its (legacy) default coder.
2023-07-05 17:11:26 -05:00
Jonathan Hefner
860271ee02 Always set :compress_threshold default value
Falsy `:compress_threshold` values are not supported, so we can always
set the default value.
2023-07-05 17:11:26 -05:00
Jonathan Hefner
416a32956c Format options doc as a description list [ci-skip] 2023-07-05 17:11:26 -05:00
Hartley McGuire
89ed0afd30
Fix MemCacheStore not warning on 6.1 cache format
While working on fixing some deprecation warnings introduced when the
6.1 cache_format deprecation was [moved][1] to be on usage, I found that
the MemCacheStore actually has its own `default_coder` method.

This adds the warning to MemCacheStore's `default_coder` method so that
every cache store will warn on using the 6.1 format.

[1]: 088551c802bb4005a667aaa33814cfbb1feb3927
2023-07-05 10:20:55 -04:00
Petrik
55190de46b Document return value of Rails.cache.delete_multi 2023-07-05 15:46:20 +02:00
Jean Boussier
cf67782e43 Fix AS::Cache 7.1 format to properly compress bare strings
The bare string and compression features weren't working properly
together.

I discovered this while trying to fix deprecation warnings.
2023-07-05 13:56:05 +02:00
Hartley McGuire
f31172582c
Add lower bound to Listen gem requirement
An issue was recently opened with the following error message:

```
.../activesupport-7.0.6/lib/active_support/evented_file_update_checker.rb:120:in `start': undefined method `wait_for_state' for #<Listen::Listener ...>
```

The issue is that the user was using Listen 3.0.8, however the
`wait_for_state` method was [added][1] in Listen 3.3.0

We can make the error message a little better by defining a lower bound
on Listen 3.3.0 (like we do for other optional dependencies):

```
.../bundler/rubygems_integration.rb:280:in `block (2 levels) in replace_gem': can't activate listen (~> 3.3), already activated listen-3.0.8. Make sure all dependencies are added to Gemfile. (Gem::LoadError)
```

There is definitely still room for improvement here, but this should
be much more helpful in figuring out that the issue is a bad Listen
version and not a bug in Rails.

[1]: 12b4fc54a9
2023-07-02 00:04:48 -04:00
John Hawthorn
ab01f9f3da Consider Symbol "JSON-ready", improve jsonify
Previously jsonify would call `.as_json` for Integer, nil, true, and
false, even though those types are considered "JSON-ready". Technically
a user could have overridden `.as_json` for these types but I can't
imagine a use case and I don't think we should support that.

I left the same behaviour of calling `.as_json` for generic "Numeric" as
that can have user subclasses where one may have implemented as_json.
This behaviour is also used for Float (which coerces
NaN/Infinity/-Infinity into nil).

This also adds Symbol to the list of "JSON-ready" types, to avoid
unnecessarily casting them to strings (possible as we no longer perform
escaping on input). The output of jsonify should never be user visible
before it is passed through JSON.generate, so I don't think this should
be a user facing
change.

This also corrects our handling of Hash to call to_s on all keys,
matching the behaviour of `.as_json` and JSON's requirement that keys
are Strings (Symbols are also permitted as JSON knows to convert them to
a String).
2023-06-30 11:38:38 -07:00
John Hawthorn
52be530755 Escape JSON output instead of string inputs
Rails performs additional escaping of strings compared to the JSON gem,
and will escape U+2028, U+2029, <, >, and &.

In JSON, the only places those characters are valid is inside of a
JSON string, so it should be equivalent (and faster) to perform the
escaping on the output.
2023-06-29 15:37:57 -07:00
John Hawthorn
66db67436d Avoid extra pass on AS::JSON.dump with no options
JSONGemEncoder.encode previously would always perform two passes. First
it would call `.as_json(options)`, but then would perform a second pass
"jsonify" to recursively call `.as_json` (this time without options)
until the data converges into a "JSON-ready" representation.

When options are not given, the second pass should be equivalent to the
first, so we can detect that, and only perform the "jsonify" step.

The only user-visible effect of this should be that we will pass no
options to `as_json` instead of an empty Hash, but implementations of
`as_json` should already be expected to accept that.
2023-06-29 15:37:57 -07:00
Hartley McGuire
088551c802
Move 6.1 cache format warning to where its used
A deprecation warning was [added][1] to ensure that applications
manually setting `config.active_support.cache_format_version` to `6.1`
will be aware that they need to migrate. However, if an app is not using
a `config.load_defaults` of `7.0` or greater, this warning will never be
triggered.

This commit moves the deprecation warning to where the `cache_format`
value gets used to cover both cases.

[1]: 2ba3ac29c36f4c6d23b1dd302584f15739ff2aff
2023-06-28 18:09:04 -04:00
Jean Boussier
2ba3ac29c3 Deprecate active_support.cache_format_version = 6.1
Once we finally get rid of it, we're no longer constrained
on the `Entry` internal representation as other coders don't
directly marshal the instance.
2023-06-28 10:52:54 +02:00
Étienne Barrié
78c7343057 Add :report behavior to ActiveSupport::Deprecation
This behavior uses the ErrorReporter to report a deprecation as a
handled error with :warning severity.
2023-06-26 16:23:19 +02:00
Christian Schmidt
11ed5b5c52 Rename Range#overlaps? to Range#overlap?
Rename for consistency with #include? and #cover?
2023-06-23 10:25:59 +02:00
zzak
dd89f600f7
🔗 Remove RDoc auto-link from Rails module everywhere 2023-06-23 10:49:30 +09:00
Hartley McGuire
8355658a56
Fix EncryptedConfiguration not behaving like Hash
Previously, `EncryptedConfiguration` was [updated][1] to use
`InheritableOptions` so that keys could be called like methods. It was
later [updated again][2] to ensure that it behaved both like a `Hash` and
`OrderedOptions`. In this second change, the `InheritableOptions`
instance was accidentally nested with another `InheritableOptions`
instance.

This continued to mostly work as expected because `InheritableOptions`
will fall back to the inner `InheritableOptions` when the outer one
doesn't have a key. However, any methods that try to treat the outer
`InheritableOptions` like it should know about all of its keys will fail
(for example, `#keys`, `#to_h`, `#to_json`, etc.)

This commit fixes the issue by removing the extraneous outer
`InheritableOptions` instance.

[1]: a6a9fed1719a3cfe47eb1566ae4d6034374fd809
[2]: 80585daf2def5bf94854be69f81e24a16ce14c55
2023-06-21 23:29:33 -04:00
Akira Matsuda
5bdd132d00
minitest is no longer a stdlib on any of the supported versions of Rubies
CRuby's bundled minitest had been removed in 2.2.0 at
7cda8222ca
2023-06-22 02:03:33 +09:00
zzak
3e8d8e67a9
Document ActiveSupport::TestCase.fixture_paths= and getter
These methods are only available when requiring the railties
"rails/test_help" file, which is included by default in the
test_helper.rb for newly generated Rails applications.

Since requiring that file is the only way, and some applications may
have removed it or not used test-unit when generating their application,
I think it's worth calling out explicitly here.
2023-06-20 15:47:12 +09:00
Sampat Badhe
3d5fa4c105
update doc to use to_fs over deprecated to_s 2023-06-19 13:18:28 +05:30
Guillermo Iguaran
811b103b3e
Merge pull request #48494 from ghiculescu/range-tofs
Support beginless and endless ranges in `to_fs`
2023-06-17 02:36:14 -07:00
Petrik
3f1526ae76 Don't show secrets for MessageEncryptor#inspect
If anyone calls a message encryptor in the console it will
show the secret of the encryptor.

By overriding the `inspect` method to only show the class name we can
avoid accidentally outputting sensitive information.

Before:

```ruby
ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm").inspect
"#<ActiveSupport::MessageEncryptor:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
```

After:

```ruby
ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm").inspect
"#<ActiveSupport::MessageEncryptor:0x0000000104888038>"
```
2023-06-16 21:14:13 +02:00
Petrik
7ecd72e5c1 Don't show contents for EncryptedConfiguration#inspect
If anyone calls `Rails.application.credentials` in the console it will
show the unencrypted contents of the credentials.

By overriding the `inspect` method to only show the class name we can
avoid accidentally outputting sensitive information.

Before:
```ruby
Rails.application.credentials.inspect
"#<ActiveSupport::EncryptedConfiguration:0x000000010d2b38e8 ... @config={:secret=>\"something secret\"} ... @key_file_contents=\"915e4ea054e011022398dc242\" ...>"
```

After:
```ruby
Rails.application.credentials.inspect
"#<ActiveSupport::EncryptedConfiguration:0x000000010d2b38e8>"
```
2023-06-16 15:35:16 +02:00
Alex
3bfede1f22 Support beginless and endless ranges in to_fs
Fixes https://github.com/rails/rails/issues/48485
2023-06-16 16:47:48 +10:00
Jonathan Hefner
bd15567fe9 Use cache :coder option to specify :message_pack
In #48104, `:message_pack` was added as a supported value for the cache
format version because the format version essentially specifies the
default coder.  However, as an API, the format version could potentially
be used to affect other aspects of serialization, such as the default
compression format.

This commit removes `:message_pack` as a supported value for the format
version, and, as a replacement, adds support for specifying
`coder: :message_pack`:

  ```ruby
  # BEFORE
  config.active_support.cache_format_version = :message_pack

  # AFTER
  config.cache_store = :redis_cache_store, { coder: :message_pack }
  ```
2023-06-11 15:30:10 -05:00
Andrew Novoselac
745976bd57 Fix defect in Enumerable#many introduced in rails/rails@d862dff
This changed `many?` to yield with `element, *args`. The causes elements that are arrays to be destructured which can lead to defective behaviour. Since the introduction of `element, *args` was for readability purposes, we can just use `*args` instead and get the same behaviour without the defect.
2023-06-06 17:41:44 -04:00
Jean Boussier
9812641891 ActiveSupport::Deprecator stop using Singleton
Followup: https://github.com/rails/rails/pull/47354

It does a bit more than just giving you a `.instance` method
it also change the behavior of dup and clone, we don't need
any of that, and `.instance` is deprecated anyway.
2023-06-05 08:43:37 +02:00
Jean Boussier
c07812cee2 Eagerly validate pool arguments in Redis and MemCache stores
Fix: https://github.com/rails/rails/issues/48352

While we should ensure instantiating the store doesn't immediately
attempt to connect, we should eagerly process arguments so that
if they are somehow invalid we fail early during boot rather than at
runtime.

Additionally, since it's common to get pool parameters from environment
variable, we can use `Integer` and `Float` so that string representations
are valid.
2023-06-02 16:01:09 +02:00
Jean Boussier
a2f685c7c0 Cleanly fallback when failing to tokenize ERB templates
Fix: https://github.com/rails/rails/issues/48319
Followup: https://github.com/rails/rails/pull/48184
2023-06-01 11:35:31 +02:00
fatkodima
e3c546eb0c Fix humanize for strings ending with id 2023-05-31 02:16:37 +03:00
Jonathan Hefner
14676466b6 Avoid double marshal with MemoryStore::DupCoder
Prior to this commit, `MemoryStore::DupCoder` called `Entry#dup_value!`
in both `dump` and `load` to guard against external mutation of cached
values.  `Entry#dup_value!` calls `Marshal.dump` then `Marshal.load` to
dup complex objects.  However, to prevent external mutation, we only
need to call `Marshal.dump` once for `DupCoder.dump` and `Marshal.load`
once for `DupCoder.load`.

This commit changes `DupCoder` to call `Marshal.dump` and `Marshal.load`
directly instead of relying on `Entry#dup_value!`, thus halving the work
done by `Marshal` when writing and reading complex objects.

__Benchmark__

  ```ruby
  # frozen_string_literal: true

  require "benchmark/ips"
  require "active_support"
  require "active_support/cache"

  LARGE_OBJECT = 100.times.map { "x" * 100 }
  LARGE_STRING = LARGE_OBJECT.join

  cache = ActiveSupport::Cache.lookup_store(:memory_store)

  Benchmark.ips do |x|
    x.report("large string") do
      cache.write("x", LARGE_STRING)
      cache.read("x")
    end

    x.report("large object") do
      cache.write("x", LARGE_OBJECT)
      cache.read("x")
    end
  end
  ```

__Before__

  ```
  Warming up --------------------------------------
          large string     2.667k i/100ms
          large object   332.000  i/100ms
  Calculating -------------------------------------
          large string     26.539k (± 1.8%) i/s -    133.350k in   5.026373s
          large object      3.336k (± 0.9%) i/s -     16.932k in   5.076458s
  ```

__After__

  ```
  Warming up --------------------------------------
          large string     2.541k i/100ms
          large object   715.000  i/100ms
  Calculating -------------------------------------
          large string     25.117k (± 1.7%) i/s -    127.050k in   5.059945s
          large object      7.111k (± 1.2%) i/s -     35.750k in   5.028267s
  ```

Closes #46403.

Co-authored-by: Breno Gazzola <breno.gazzola@gmail.com>
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2023-05-25 16:13:04 -05:00
zzak
38bef29064
Replace all occurrences of '<tt>(\w+::\w+::\w+)</tt>' with '+$1+'
E.g.:

* <tt>Rails::Command::NotesCommand</tt> -> +Rails::Command::NotesCommand+

Co-authored-by: Hartley McGuire <skipkayhil@gmail.com>
2023-05-25 06:56:17 +09:00
zzak
e3c73fd183
Replace all occurrences of '<tt>(\w+::\w+)</tt>' with '+$1+'
E.g.:

* <tt>ActiveRecord::Base</tt> -> +ActiveRecord::Base+

Co-authored-by: Hartley McGuire <skipkayhil@gmail.com>
Co-authored-by: Petrik de Heus <petrik@deheus.net>
2023-05-25 06:52:32 +09:00
Mike Dalessio
aea8849821 ERB::Util.html_escape_once always returns an html_safe string
This method previously maintained the `html_safe?` property of a string on the return
value. Because this string has been escaped, however, not marking it as `html_safe` causes
entities to be double-escaped.

As an example, take this view snippet:

  ```html
  <p><%= html_escape_once("this & that &amp; the other") %></p>
  ```

Before this change, that would be double-escaped and render as:

  ```html
  <p>this &amp;amp; that &amp;amp; the other</p>
  ```

After this change, it renders correctly as:

  ```html
  <p>this &amp; that &amp; the other</p>
  ```

[Fix #48256]
2023-05-22 12:02:03 +02:00
Mike Dalessio
4b6894168b
Deprecate SafeBuffer#clone_empty
This method is unused within the Rails codebase, the last caller was
removed by 479c7cac in 2014 (Rails 4.2.0).
2023-05-20 12:45:43 -04:00
Hartley McGuire
b3c6a9adf0
Remove explicit "aliased as" documentation
Most of these are redundant because rdoc handles these itself, but
`titlecase` on `ActiveSupport::Inflector` does not exist so that one is
just incorrect.
2023-05-18 11:04:04 -04:00
Jonathan Hefner
76ea586c6a Handle non-ASCII-only in AS::MessagePack#signature?
Because `ActiveSupport::MessagePack::Serializer::SIGNATURE` includes a
non-ASCII-only byte (`"\xCC"`), it raises `Encoding::CompatibilityError`
when compared with another string that is not encoded with
`Encoding::BINARY` and also includes a non-ASCII-only byte.

To prevent that, this commit changes `AS::MessagePack#signature?` to
directly compare the first two bytes of both strings.

Fixes #48196.
2023-05-11 21:13:45 -05:00
Juan Manuel Ramallo
1a63f9410d [Documentation] ActiveSupport::InheritableOptions with string keys
Updated the ActiveSupport::InheritableOptions documentation to exemplify
how to use a parent hash that contains string keys.

    parent = { "foo" => true }
    child = ActiveSupport::InheritableOptions.new(parent.symbolize_keys)
    child.foo # => true

This example is helpful because ActiveSupport::OrderedOptions only deals
with symbol keys and this implementation detail is hidden from the API
docs.

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2023-05-10 13:05:40 -05:00
Jonathan Hefner
af6d83521c Support :message_pack as message serializer
This commit adds support for `:message_pack` as a serializer for
`MessageEncryptor` and `MessageVerifier`, and, consequently, as an
option for `config.active_support.message_serializer`.

The `:message_pack` serializer is implemented via
`ActiveSupport::Messages::SerializerWithFallback` and can fall back to
deserializing with `AS::JSON`.  Additionally, the `:marshal`, `:json`,
and `:json_allow_marshal` serializers can now fall back to deserializing
with `AS::MessagePack`.

This commit also adds support for `:message_pack_allow_marshal` as a
serializer, which can fall back to deserializing with `Marshal` as well
as `AS::JSON`.
2023-05-08 14:39:00 -05:00
Jonathan Hefner
4143c0b3e8 Default message serializer to :json_allow_marshal
Prior to this commit, `config.load_defaults 7.1` would cause old
messages (or messages from older apps) to become unreadable, and
developers were encouraged to manually set
`config.active_support.message_serializer = :json_allow_marshal` in
order to prevent this.

This commit changes the default message serializer set by
`config.load_defaults 7.1` from `:json` to `:json_allow_marshal` so that
upgraded apps can continue to read old messages without additional
configuration.

The intention is to eventually change the default to `:json` (with no
`Marshal` fallback) in Rails 7.2, after some time has passed with apps
generating JSON-serialized messages.

Apps can opt in to JSON-only serialization before Rails 7.2 by manually
setting `config.active_support.message_serializer = :json`.

Fixes #48118.
2023-05-08 12:41:53 -05:00
Jonathan Hefner
9fbfd8100b Unify Message{Encryptor,Verifier} serializer config
In #42843 and #42846, several config settings were added to control the
default serializer for `MessageEncryptor` and `MessageVerifier`, and to
provide a migration path from a default `Marshal` serializer to a
default `JSON` serializer:

* `config.active_support.default_message_encryptor_serializer`
  * Supports `:marshal`, `:hybrid`, or `:json`.
* `config.active_support.default_message_verifier_serializer`
  * Supports `:marshal`, `:hybrid`, or `:json`.
* `config.active_support.fallback_to_marshal_deserialization`
  * Affects `:hybrid` for both `MessageEncryptor` and `MessageVerifier`.
* `config.active_support.use_marshal_serialization`
  * Affects `:hybrid` for both `MessageEncryptor` and `MessageVerifier`.

This commit unifies those config settings into a single setting,
`config.active_support.message_serializer`, which supports `:marshal`,
`:json_allow_marshal`, and `:json` values.  So, for example,

  ```ruby
  config.active_support.default_message_encryptor_serializer = :hybrid
  config.active_support.default_message_verifier_serializer = :hybrid
  config.active_support.fallback_to_marshal_deserialization = true
  config.active_support.use_marshal_serialization = false
  ```

becomes

  ```ruby
  config.active_support.message_serializer = :json_allow_marshal
  ```

and

  ```ruby
  config.active_support.default_message_encryptor_serializer = :hybrid
  config.active_support.default_message_verifier_serializer = :hybrid
  config.active_support.fallback_to_marshal_deserialization = false
  config.active_support.use_marshal_serialization = false
  ```

becomes

  ```ruby
  config.active_support.message_serializer = :json
  ```

This commit also replaces `ActiveSupport::JsonWithMarshalFallback` with
`ActiveSupport::Messages::SerializerWithFallback`, which implements a
generic mechanism for serializer fallback.  The `:marshal` serializer
uses this mechanism too, so

  ```ruby
  config.active_support.default_message_encryptor_serializer = :hybrid
  config.active_support.default_message_verifier_serializer = :hybrid
  config.active_support.fallback_to_marshal_deserialization = false
  config.active_support.use_marshal_serialization = true
  ```

becomes

  ```ruby
  config.active_support.message_serializer = :marshal
  ```

Additionally, the logging behavior of `JsonWithMarshalFallback` has been
replaced with notifications which include the names of the intended and
actual serializers, as well as the serialized and deserialized message
data.  This provides a more targeted means of tracking serializer
fallback events.  It also allows the user to "silence" such events, if
desired, without an additional config setting.

All of these changes make it easier to add migration paths for new
serializers such as `ActiveSupport::MessagePack`.
2023-05-08 12:09:45 -05:00
Jonathan Hefner
bda3539053
Merge pull request #48150 from jonathanhefner/cache-summarize-logged-multi-keys
Log key summary for `*_multi` cache operations
2023-05-07 22:21:49 -05:00