In Rails 7, if you do `Rails.cache.write(key, value, expires_in: 1.minute.from_now)`, it will work. The actual expiration will be much more than a minute away, but it won't raise. (The correct code is `expires_in: 1.minute` or `expires_at: 1.minute.from_now`.)
Since https://github.com/rails/rails/pull/45892 the same code will error with:
```
NoMethodError: undefined method `negative?' for 2008-04-24 00:01:00 -0600:Time
/Users/alex/Code/rails/activesupport/lib/active_support/cache.rb:743:in `merged_options'
/Users/alex/Code/rails/activesupport/lib/active_support/cache.rb:551:in `write'
```
To make it a bit easier to upgrade to Rails 7.1, this PR introduces a better error if you pass a `Time` object to `expires_in:`
```
ArgumentError: expires_in parameter should not be a Time. Did you mean to use expires_at? Got: 2023-04-07 14:47:45 -0600
/Users/alex/Code/rails/activesupport/lib/active_support/cache.rb:765:in `handle_invalid_expires_in'
/Users/alex/Code/rails/activesupport/lib/active_support/cache.rb:745:in `merged_options'
/Users/alex/Code/rails/activesupport/lib/active_support/cache.rb:551:in `write'
```
3d00c8b97f introduced a regression:
```ruby
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym("API")
end
```
If you have a controller called `APIController`, you may get a crash backtrace that looks like this:
```
FrozenError: can't modify frozen String: "API"
rails (b8a399cd6ffe) actionpack/lib/action_dispatch/http/request.rb:89:in `controller_class_for'
rails (b8a399cd6ffe) actionpack/lib/action_dispatch/request/utils.rb💯in `action_encoding_template'
rails (b8a399cd6ffe) actionpack/lib/action_dispatch/request/utils.rb:85:in `encode'
rails (b8a399cd6ffe) actionpack/lib/action_dispatch/request/utils.rb:45:in `set_binary_encoding'
rails (b8a399cd6ffe) actionpack/lib/action_dispatch/http/parameters.rb:68:in `path_parameters='
```
And you can see the issue in action like this:
```ruby
Tanda @ Rails 7.1.0.alpha (test) :004 > "API".freeze.underscore.frozen?
false # this is expected
Tanda @ Rails 7.1.0.alpha (test) :004 > "API".freeze.underscore.camelize.frozen?
false # this is *not* expected, and is what causes the above code to crash
```
I think the correct behaviour is for the inflector to always return non-frozen strings, even if it's using a cached frozen string as is the case in 3d00c8b97f
Other semi-related PRs: https://github.com/rails/rails/pull/41174, https://github.com/rails/rails/pull/41881
cc @amatsuda @byroot
`Listen.to` starts a bunch of background threads that need to perform
some work before they are able to receive events, but it doesn't block
until they are ready, which expose us to a race condition.
With `wait_for_state(:processing_events)` we can ensure that it's ready on
Linux, however on macOS, the Darwin backend has a second background thread
we can't wait on.
As a workaround we wait a bit after the fork to allow that thread to
reach it's listning state.
Fanout is a singleton shared among threads, so all of its hash maps
should be concurrent hash maps otherwise we'll see errors like this:
```
RuntimeError: can't add a new key into hash during iteration
```
Currently when opening the main framework pages there is no introduction
to the framework. Instead we only see a whole lot of modules and the
`gem_version` and `version` methods.
By including the READMEs using the `:include:` directive each frameworks
has a nice introduction.
For markdown READMEs we need to add the :markup: directive.
[ci-skip]
Co-authored-by: zzak <zzakscott@gmail.com>
All methods called on ActiveSupport::Deprecation are delegated to the
singleton instance, but now that each framework has its own Deprecation
instance, it's no longer necessary for internal deprecations.
For external code, instead of using ActiveSupport::Deprecation.warn,
they should create their own instance of the class and call warn on it.
Use case
A very common pattern in Ruby, especially in testing is to save the value of an attribute, set a new value, and then restore the old value in an `ensure` clause.
e.g. in unit tests
```ruby
def test_something_when_enabled
enabled_was, SomeLibrary.enabled = SomeLibrary.enabled, true
# test things
ensure
SomeLibrary.enabled = enabled_was
end
```
Or sometime in actual APIs:
```ruby
def with_something_enabled
enabled_was = @enabled
@enabled = true
yield
ensure
@enabled = enabled_was
end
```
There is no inherent problem with this pattern, but it can be easy to make a mistake, for instance the unit test example:
```ruby
def test_something_when_enabled
some_call_that_may_raise
enabled_was, SomeLibrary.enabled = SomeLibrary.enabled, true
# test things
ensure
SomeLibrary.enabled = enabled_was
end
```
In the above if `some_call_that_may_raise` actually raises, `SomeLibrary.enabled` is set back to `nil` rather than its original value. I've seen this mistake quite frequently.
Object#with
I think it would be very useful to have a method on Object to implement this pattern in a correct and easy to use way.
NB: `public_send` is used because I don't think such method should be usable if the accessors are private.
With usage:
```ruby
def test_something_when_enabled
SomeLibrary.with(enabled: true) do
# test things
end
end
```
```ruby
GC.with(measure_total_time: true, auto_compact: false) do
# do something
end
```
Lots of tests in Rails's codebase could be simplified, e.g.:
- Changing `Thread.report_on_exception`: 2d2fdc941e/activerecord/test/cases/connection_pool_test.rb (L583-L595)
- Changing a class attribute: 2d2fdc941e/activerecord/test/cases/associations/belongs_to_associations_test.rb (L136-L150)
Why:
----
Following up on [#47323](https://github.com/rails/rails/issues/47323).
Many options are not forwarded to the Dalli client when it is
initialized from the `ActiveSupport::Cache::MemCacheStore`. This is to
support a broader set of features powered by the implementation. When an
instance of a client is passed on the initializer, it takes precedence,
and we have no control over which attributes will be overridden or
re-processed on the client side; this is by design and should remain as
such to allow both projects to progress independently. Having this
option introduces several potential bugs that are difficult to pinpoint
and get multiplied by which version of the tool is used and how each
evolves. During the conversation on the issue, the `Dalli` client
maintainer supports [deprecating](https://github.com/rails/rails/issues/47323#issuecomment-1424292456)
this option.
How:
----
Removing this implicit dependency will ensure each library can evolve
separately and cements the usage of `Dalli::Client` as an [implementation
detail](https://github.com/rails/rails/issues/21595#issuecomment-139815433)
We can not remove a supported feature overnight, so I propose we add a
deprecation warning for the next minor release(7.2 at this time).
There was a constant on the `Cache` namespace only used to restrict
options passed to the `Dalli::Client` initializer that now lives on the
`MemCacheStore` class.
Co-authored-by: Eileen M. Uchitelle <eileencodes@users.noreply.github.com>