Commit Graph

7932 Commits

Author SHA1 Message Date
Jean Boussier
27140247c2 Cleanup defined? usage
Now that we dropped support for Ruby 2.7, we no longer
need to check if variables are defined before accessing them
to avoid the undefined variable warning.
2024-01-05 15:05:35 +01:00
Jean Boussier
ef65e5fb32 Cleanup usage of ruby2_keywords
Now that we no longer support Ruby 2.7, many `ruby2_keyword` calls
can be eliminated.

The ones that are left could be eliminated but would end up substantially
slower or more compliacated so I left them for now.
2024-01-05 14:40:18 +01:00
Junichi Sato
de77bca9c5
Remove workaround for Ruby 2.7 at ActiveSupport::Cache#lookup_store
Since the minimum required version is now 3.1,
this can be got rid of like the comment commands.
2024-01-05 12:22:08 +09:00
Sean Doyle
7abaeea4d3 Remove MethodCallAssertions Ruby 2.7 work-around
By dropping support for 2.7, Rails 8 will not need to work-around
positional and keyword argument splatting.
2024-01-04 11:10:30 -05:00
Rafael Mendonça França
28997e4cda
Make sure isolation tests are properlly parallelized
When `parallelize` is used, and the ActiveSupport::Testing::ParallelizeExecutor
is being set, we can't call `parallelize_me!` ourselves since the
parallel executor might not be set to run tests in parallel.

In fact, maybe we should stop calling `parallelize_me!` altogether here
and rely on `parallelize` to do the right thing, but this might break
test classes that are using `ActiveSupport::Testing::Isolation` outside
without calling `parallelize`.
2024-01-04 02:43:31 +00:00
Jonathan Hefner
65087c8382 Use logical core count for test parallelization
This changes the default number of workers when parallelizing tests to
be based on logical core count instead of physical core count.  This may
provide a performance boost for parallelized tests.

This also reverts #50545 in favor of using the default worker count.
2024-01-03 16:13:47 -06:00
Rafael Mendonça França
8b4e92f4be
Point rubocop to ruby 3.1 2024-01-03 19:02:32 +00:00
Rafael Mendonça França
9d18dc8505
Remove all code to work with Ruby < 3.1 2024-01-03 19:02:31 +00:00
Jonathan Hefner
ad2354e6c6 Switch to new enum syntax in example code [ci-skip] 2024-01-01 21:31:11 -06:00
Santiago Bartesaghi
78a56cc12a Remove unnecessary require 2023-12-31 17:12:31 -03:00
Jean Boussier
009920814b
Merge pull request #50488 from wjessop/main
Move singleton require
2023-12-31 09:15:20 +01:00
Hartley McGuire
97736d7628
Merge pull request #50498 from skipkayhil/hm-doc-write-options
Document Cache::WriteOptions [ci-skip]
2023-12-31 00:45:26 -05:00
Hartley McGuire
f721e719b7
Document Cache::WriteOptions 2023-12-31 00:36:19 -05:00
Hartley McGuire
f042e025c1
Remove self referential links from Cache::Store
Also swap some method references to code blocks because they aren't
documented (so the auto-references don't work).
2023-12-30 23:57:50 -05:00
Hartley McGuire
7ce5d10398
Improve RedisCacheStore docs
- tweak opening paragraph to specifically mention that the Redis backing
  a Cache should not be the Redis backing Active Job
- add code blocks to Redis::Distributed since it uses the class syntax
- expand on ways to point Cache at a Redis in initialization
- add an rdoc link for #fetch and a code block for `-amount`
2023-12-30 23:38:38 -05:00
Hartley McGuire
8ee46636b7
Remove self referential links from MemoryStore 2023-12-30 23:05:34 -05:00
Hartley McGuire
285ad7bf36
Remove self referential links from MemCacheStore 2023-12-30 22:56:46 -05:00
Manish Sharma
94a4adb8a1 [FIX] Fix Activesupport json encode for hash 2023-12-30 20:22:11 +05:30
Will Jessop
7606002f13 Move singleton require.
ActiveSupport::Deprecation used to `include Singleton` which is why the require is here, but it was removed in 9812641891f1c9ba4c3f8ffb8549ec26fc2668c4 (June 5 2023). Leaving the require seems to have been an oversight.

However, module ActiveJob::Serializers::ObjectSerializer does require Singleton and was relying on AS:Deprecation to require it. This PR just moves the require to the place it's actually used.
2023-12-30 12:02:01 +00:00
Hartley McGuire
f93eb16564
Improve Deprecation API docs and guide
- Link to Deprecation::Behavior in configuring guide

  The current list of options for `config.active_support.deprecation`
  was missing the newly added `:report` option. Instead of adding the
  missing option and continuing to keep 4 different lists of the same
  options in sync, I opted to replace the list with a link to the
  options in the Behavior API docs. This had the additional advantage of
  giving more information about all of the options which was not
  mentioned in the Configuring guide.

- Use symbols for Behavior options

  It felt to me like naming the options did not make it explicit that
  those were the symbols to pass to `#behavior=`, but by adding the `:`
  that becomes more clear.

- Add some API links

  There were a few references to `behavior=`, but we may as well link to
  the actual method.
2023-12-14 17:10:44 -05:00
Mark Oleson
d5c7f7cc06 fix LocalCache#read_multi_entries not namespacing keys before looking them up in the cache 2023-12-11 11:25:05 -06:00
Jean Boussier
8e1c1ccb29 Optimize many delegated methods on ActiveSupport::Duration
Fix: https://github.com/rails/rails/pull/50136

Using delegate saves on the `method_missing + public_send` combo.

I chose to delegate to all the methods I saw called in Rails own test
suite, but there is likely a handful more candidates for explicit delegation.

But also this rely on a new (private) parameter in `Module#delegate` to
provide the expected delegated type and use that to define delegators
with the extact required signature. This saves on array and hash allocations
caused by splatting.

In some ways it's a continuation of https://github.com/rails/rails/pull/46875

Note that I didn't make the new `as:` parameter public, as I fear it's
a bit too brittle to be used. For the same reason I'm considering reverting
the optimized path behavior on `to: :class` and requiring to explictly pass
`as: self` for that optimized path.

Co-Authored-By: Alexandre Terrasa <alexandre.terrasa@shopify.com>
2023-12-08 15:41:05 +01:00
Jean Boussier
be258503ac Module#delegate takes a new private as parameter
This is a continuation of https://github.com/rails/rails/pull/46875

The behavior of looking up the class method when `to: :class` is passed
is a bit error prone because it silently degrades.

By passing the expected owner of the delegated method, we can be more
strict, and also generate a delegator in a module rather than having
to do it at inclusion time.

I made this argument private API because we want it in Rails, but
I'm worried it might be a bit too sharp for public API. I can
be convinced otherwise though.
2023-12-08 15:09:59 +01:00
Jean Boussier
b979afe75d
Merge pull request #48957 from cmaruz/48326
Better handle SyntaxError in Action View
2023-12-05 21:00:06 +01:00
Jean Boussier
7cfc4ee676 Optimize Time.at_with_coercion
By using `ruby2_keyword` style delegation we we can avoid a
few allocations and some extra checks.

```
$ ruby --yjit /tmp/bench-as-time-at.rb
ruby 3.2.2 (2023-03-30 revision e51014f9c0) +YJIT [arm64-darwin22]
=== Complex call ====
Warming up --------------------------------------
        Time.without   320.514k i/100ms
           Time.with    68.433k i/100ms
       Time.opt_with   167.532k i/100ms
Calculating -------------------------------------
        Time.without      3.781M (± 4.8%) i/s -     18.910M in   5.014574s
           Time.with      1.586M (± 3.5%) i/s -      7.938M in   5.010525s
       Time.opt_with      2.003M (± 2.4%) i/s -     10.052M in   5.021309s

Comparison:
        Time.without:  3781330.9 i/s
       Time.opt_with:  2003025.9 i/s - 1.89x  slower
           Time.with:  1586289.9 i/s - 2.38x  slower

Time.without: 2.003 alloc/iter
Time.with: 9.002 alloc/iter
Time.opt_with: 7.002 alloc/iter

=== Simple call ====
Warming up --------------------------------------
        Time.without   749.097k i/100ms
           Time.with   342.855k i/100ms
       Time.opt_with   416.063k i/100ms
Calculating -------------------------------------
        Time.without      9.289M (± 3.4%) i/s -     46.444M in   5.005361s
           Time.with      3.601M (± 2.1%) i/s -     18.171M in   5.048794s
       Time.opt_with      4.373M (± 8.1%) i/s -     22.051M in   5.084967s

Comparison:
        Time.without:  9289271.2 i/s
       Time.opt_with:  4373226.2 i/s - 2.12x  slower
           Time.with:  3600733.6 i/s - 2.58x  slower

Time.without: 1.002 alloc/iter
Time.with: 3.001 alloc/iter
Time.opt_with: 3.002 alloc/iter
```

```ruby
require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'activesupport', require: 'active_support/all', github: 'rails/rails'
  gem 'benchmark-ips'
end

class Time
  class << self
    def opt_at_with_coercion(time_or_number, *args)
      if args.empty?
        if time_or_number.is_a?(ActiveSupport::TimeWithZone)
          at_without_coercion(time_or_number.to_r).getlocal
        elsif time_or_number.is_a?(DateTime)
          at_without_coercion(time_or_number.to_f).getlocal
        else
          at_without_coercion(time_or_number)
        end
      else
        at_without_coercion(time_or_number, *args)
      end
    end
    ruby2_keywords :opt_at_with_coercion
  end
end

puts RUBY_DESCRIPTION

puts "=== Complex call ===="
Benchmark.ips do |x|
  x.report("Time.without") do
    ::Time.at_without_coercion(223423423, 32423423, :nanosecond, in: "UTC")
  end

  x.report("Time.with") do
    ::Time.at_with_coercion(223423423, 32423423, :nanosecond, in: "UTC")
  end

  x.report("Time.opt_with") do
    ::Time.opt_at_with_coercion(223423423, 32423423, :nanosecond, in: "UTC")
  end

  x.compare!(order: :baseline)
end

def measure_allocs(title, iterations: 1_000)
  before = GC.stat(:total_allocated_objects)
  iterations.times do
    yield
  end
  allocs = GC.stat(:total_allocated_objects) - before
  puts "#{title}: #{allocs.to_f / iterations} alloc/iter"
end

measure_allocs("Time.without") do
  ::Time.at_without_coercion(223423423, 32423423, :nanosecond, in: "UTC")
end

measure_allocs("Time.with") do
  ::Time.at_with_coercion(223423423, 32423423, :nanosecond, in: "UTC")
end

measure_allocs("Time.opt_with") do
  ::Time.opt_at_with_coercion(223423423, 32423423, :nanosecond, in: "UTC")
end

puts "=== Simple call ===="
Benchmark.ips do |x|
  x.report("Time.without") do
    ::Time.at_without_coercion(223423423)
  end

  x.report("Time.with") do
    ::Time.at_with_coercion(223423423)
  end

  x.report("Time.opt_with") do
    ::Time.opt_at_with_coercion(223423423)
  end

  x.compare!(order: :baseline)
end

def measure_allocs(title, iterations: 1_000)
  before = GC.stat(:total_allocated_objects)
  iterations.times do
    yield
  end
  allocs = GC.stat(:total_allocated_objects) - before
  puts "#{title}: #{allocs.to_f / iterations} alloc/iter"
end

measure_allocs("Time.without") do
  ::Time.at_without_coercion(223423423)
end

measure_allocs("Time.with") do
  ::Time.at_with_coercion(223423423)
end

measure_allocs("Time.opt_with") do
  ::Time.opt_at_with_coercion(223423423, 32423423)
end
```
2023-12-05 12:42:40 +01:00
Mario Caropreso
df6d2fbf2e Addressed an issue where syntax errors generated inside an eval
method cause an Internal Server Error due to a TypeError.

In order to display the extraced source of a syntax error, we try
to locate the node id for the backtrace location. The call to
RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location is expecting
a Thread::Backtrace::Location object, but we are passing a
SourceMapLocation instead.

This commit does two things:
1) it addresses the issue by making sure that we are always passing
a Thread::Backtrace::Location instead
2) it allows the development view to show the extracted source fragment
2023-12-05 06:40:19 +00:00
Aleksei Chernenkov
aedb808829 Fix Time.now/DateTime.now/Date.today to return results in a system timezone after #travel_to
There is a bug in the current implementation of #travel_to:
it remembers a timezone of its argument, and all stubbed methods start
returning results in that remembered timezone. However, the expected
behaviour is to return results in a system timezone.

It can lead to bugs in tests like this one:
https://github.com/faker-ruby/faker/issues/2861
2023-12-02 10:05:31 +01:00
Hartley McGuire
3d002c1947
Fix test infra depending on CI=true
The CI env var was recently [changed][1] to be specific for the
application being tested instead of the framework tests. Because of
this, these lines which should have been true before are now false.

[1]: 1f0262aa2b768b14de5e6ef29d0e547628f570ee
2023-11-30 11:11:19 -05:00
Kevin McPhillips
3d60490a9d Add #to_s and pretty print for ActiveSupport::InheritableOptions 2023-11-28 17:49:15 -05:00
Jonathan Hefner
ea46a00a9f
Merge pull request #50192 from sbfaulkner/memory-store-unless-exist
Fix MemoryStore#write with unless_exist and namespace
2023-11-28 12:25:29 -06:00
S. Brent Faulkner
701377c6af
Fix MemoryStore#write with unless_exist and namespace
Updates `MemoryStore#write_entry` to pass a `nil` `namespace` to
`exist?`, which expects a _name_ rather than a an already "normalized"
_key_. This fixes a bug where `unless_exist` would overwrite any
existing entry if a `namespace` was used.
2023-11-28 13:08:55 -05:00
Kevin McPhillips
9b03df5626 Add some behaviour to ActiveSupport::InheritableOptions to make it quack more like a hash 2023-11-28 12:01:10 -05:00
Jean Boussier
b07362cffa ActiveSupport::Testing::Isolation: gracefully handle the subprocess dying
Right now if the subprocess exit uncleanly, it straight out bring the
parent down with it because it will fail to parse the (likely empty)
Marshal payload:

```
<internal:marshal>:34:in `load': marshal data too short (ArgumentError)
	from 3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:23:in `run'
	from 3.3.0+0/gems/minitest-5.20.0/lib/minitest.rb:1094:in `run_one_method'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:179:in `block in run'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:168:in `with_timestamps'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:178:in `run'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:229:in `block in run_from_queue'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/ci/queue/redis/worker.rb:55:in `poll'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:228:in `run_from_queue'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:213:in `__run'
	from 3.3.0+0/gems/minitest-5.20.0/lib/minitest.rb:162:in `run'
	from 3.3.0+0/gems/minitest-5.20.0/lib/minitest.rb:86:in `block in autorun'
 - XXXX::XXXXTest#test_xxxxx - /tmp/bundle/ruby/3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:52:in `write': closed stream (IOError)
	from 3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:52:in `puts'
	from 3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:52:in `block (2 levels) in run_in_isolation'
	from 3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:32:in `fork'
	from 3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:32:in `block in run_in_isolation'
	from 3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:28:in `pipe'
	from 3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:28:in `run_in_isolation'
	from 3.3.0+0/bundler/gems/rails-488a7ce18880/activesupport/lib/active_support/testing/isolation.rb:19:in `run'
	from 3.3.0+0/gems/minitest-5.20.0/lib/minitest.rb:1094:in `run_one_method'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:179:in `block in run'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:168:in `with_timestamps'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:178:in `run'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:229:in `block in run_from_queue'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/ci/queue/redis/worker.rb:55:in `poll'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:228:in `run_from_queue'
	from 3.3.0+0/gems/ci-queue-0.38.0/lib/minitest/queue.rb:213:in `__run'
	from 3.3.0+0/gems/minitest-5.20.0/lib/minitest.rb:162:in `run'
	from 3.3.0+0/gems/minitest-5.20.0/lib/minitest.rb:86:in `block in autorun'
```

This breaks the Minitest contract that `run_one_method` shouldn't raise
ever, and return a `Minitest::Result`.

By properly checking the sub process status, we can turn this crash into
a test failure, allowing the original test process to go on.
2023-11-28 14:03:59 +01:00
Jonathan Hefner
04f29cb134 Format inline code [ci-skip] 2023-11-23 11:56:56 -06:00
Jean Boussier
2f19782dce ErrorReporter#unexpected to report in production but raise in development
It's a common useful pattern for situation where something isn't
supposed to happen, but if it does we can recover from it.

So in such situation you don't want such issue to be hidden
in development or test, as it's likely a bug, but do not want to
fail a request if it happens in production.

In other words, it behaves like `#record` in development and test
environments, and like `raise` in production.

Fix: https://github.com/rails/rails/pull/49638
Fix: https://github.com/rails/rails/pull/49339

Co-Authored-By: Andrew Novoselac <andrew.novoselac@shopify.com>
Co-Authored-By: Dustin Brown <dbrown9@gmail.com>
2023-11-20 09:42:56 +01:00
Adam Renberg Tamm
2251a4ba07 Adjust instr. for Cache::Store#fetch_multi so writes are after reads 2023-11-17 11:33:07 +01:00
Sander Verdonschot
4cfdcfef8e
Make return values of Cache::Store#write consistent
The return value was not specified before. Now it returns `true` on a
successful write, `nil` if there was an error talking to the cache
backend, and `false` if the write failed for another reason (e.g. the
key already exists and `unless_exist: true` was passed).
2023-11-16 08:54:23 -05:00
Jean Boussier
514a903431 Fix next Rails version in a deprecation message 2023-11-14 13:12:34 +01:00
Jean Boussier
37b0c603d5 Formally deprecate passing caller to Deprecation#warn
This emitted a warning since 2015, but it's likely most
offenders never saw it.

Ref: 211f55d4fd
2023-11-14 10:33:15 +01:00
Jean Boussier
c2be3ea65c ActiveSupport::Deprecation handle blaming generated code
Fix: #50047

`Backtrace::Location` instance for code generated with eval always
have their `absolute_path` set to `nil`. So if absolute path is nil
we should fallback to checking `#path`.

Co-Authored-By: fatkodima <fatkodima123@gmail.com>
2023-11-14 09:51:41 +01:00
Hartley McGuire
9af99bfffc Fix logged cache key normalization
Previously, the `Cache::Store` instrumentation would call
`normalize_key` when adding a key to the log. However, this resulted in
the logged key not always matching the actual key written/read from the
cache:

```irb
irb(main):004> cache.write("foo", "bar", namespace: "baz")
D, [2023-11-10T12:44:59.286362 #169586] DEBUG -- : Cache write: baz:foo ({:compress=>false, :compress_threshold=>1024, :namespace=>"baz"})
=> true
irb(main):005> cache.delete("foo", namespace: "baz")
D, [2023-11-10T12:45:03.071300 #169586] DEBUG -- : Cache delete: foo
=> true
```

In this example, `#write` would correctly log that the key written to
was `baz:foo` because the `namespace` option would be passed to the
`instrument` method. However, other methods like `#delete` would log
that the `foo` key was deleted because the `namespace` option was _not_
passed to `instrument`.

This commit fixes the issue by making the caller responsible for passing
the correct key to `#instrument`. This allows `normalize_key` to be
removed from the log generation which both prevents the key from being
normalized a second time and removes the need to pass the full options
hash into `#instrument`.

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2023-11-11 12:56:39 -06:00
Jean Boussier
7ae4a5f3b5 Automatically eager load TZInfo
On the first call it can be a bit slow because it needs to load
a bunch of data. It's also better to do it as part of boot
so that some of that data is shared via Copy-on-Write
2023-11-09 18:44:27 +01:00
fatkodima
096201f87d Fix RedisCacheStore#write_multi with :expires_in 2023-11-08 23:27:57 +02:00
John Hawthorn
42ad4d6b0b Eager load ActiveSupport::Callback procs
Follow up to previous commit where these procs were allows to be shared
between subclasses. This change allocates the procs as they are first
declared.
2023-11-07 16:19:00 -08:00
Jean Boussier
d36eb23976 Specialize various #present? implementations
Because `#present?` always resolve to `Object#present?`, it's
an extremely polymorphic method, and inline cache hits are low.

In addition, it requires an extra call to `self.blank?` which is
an overhead.

By specializing `present?` on common types, we avoid both of these
slow-downs:

```

ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
Warming up --------------------------------------
            present?   198.028k i/100ms
        opt_present?   565.521k i/100ms
Calculating -------------------------------------
            present?      2.087M (± 8.8%) i/s -     10.297M in   5.028398s
        opt_present?      5.584M (± 8.6%) i/s -     27.711M in   5.023852s

Comparison:
            present?:  2086621.6 i/s
        opt_present?:  5584373.5 i/s - 2.68x  faster
```

```
ruby 3.2.2 (2023-03-30 revision e51014f9c0) +YJIT [arm64-darwin22]
Warming up --------------------------------------
            present?   819.792k i/100ms
        opt_present?     1.047M i/100ms
Calculating -------------------------------------
            present?     12.192M (± 8.8%) i/s -     60.665M in   5.050622s
        opt_present?     16.540M (± 8.2%) i/s -     82.676M in   5.059029s

Comparison:
            present?: 12192047.5 i/s
        opt_present?: 16539689.6 i/s - 1.36x  faster
```

```ruby

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'benchmark-ips'
  gem 'activesupport'
end

require 'active_support/all'

class Object
  def opt_present?
    respond_to?(:empty?) ? !empty? : !!self
  end
end

class NilClass
  def opt_present?
    false
  end
end

class FalseClass
  def opt_present?
    false
  end
end

class TrueClass
  def opt_present?
    true
  end
end

class Array
  def opt_present?
    !empty?
  end
end

class Hash
  def opt_present?
    !empty?
  end
end

class Symbol
  def opt_present?
    !empty?
  end
end

class String
  def opt_present?
    !blank?
  end
end

class Numeric # :nodoc:
  def opt_present?
    true
  end
end

class Time # :nodoc:
  def opt_present?
    true
  end
end

array = []
hash = {}
time = Time.now

puts RUBY_DESCRIPTION
Benchmark.ips do |x|
  x.report("present?") do
    true.present?
    false.present?
    1.present?
    1.0.present?
    array.present?
    hash.present?
    :foo.present?
    time.present?
  end

  x.report("opt_present?") do
    true.opt_present?
    false.opt_present?
    1.opt_present?
    1.0.opt_present?
    array.opt_present?
    hash.opt_present?
    :foo.opt_present?
    time.opt_present?
  end

  x.compare!(order: :baseline)
end
```
2023-11-04 08:45:51 +01:00
Jean Boussier
9ade3f9b56
Merge pull request #49669 from intrip/fix-message-metadata-non-str
Fix decoding data encoded using a non-String purpose
2023-11-01 11:22:36 +01:00
Jean Boussier
db92ea32e0 Simplify attr_internal_define
The `@` prefix is always stripped, so might as well not require it.

For backward compatibility reasons we still handle the prefix for now,
but we eagerly strip it and emit a deprecation.
2023-10-31 13:42:57 +01:00
Petrik de Heus
89b0a8cb24
Merge pull request #49653 from Earlopain/add_filter-example
Better example for backtrace filter add_filter
2023-10-30 22:14:21 +01:00
Philippe Creux
2b3a002a2a Improve error messages of assert_changes and assert_no_changes 2023-10-30 13:30:33 -05:00
John Hawthorn
b897b4c164
Merge pull request #49784 from jhawthorn/notification_exception_groups
Fix exception guards on multiple subscriber types
2023-10-30 08:25:11 -07:00
Jonathan Hefner
bb17d787c8 Ensure {down,up}case_first returns non-frozen string
Prior to this commit, `String#downcase_first` and `String#upcase_first`
would return a frozen string when called on an empty string:

  ```ruby
  # BEFORE
  "foo".downcase_first.frozen? # => false
  "".downcase_first.frozen?    # => true

  # AFTER
  "foo".downcase_first.frozen? # => false
  "".downcase_first.frozen?    # => false
  ```
2023-10-29 12:28:06 -05:00
Jean Boussier
c28e4f2434 Use double quotes more consistenly in doc and error messages
For better or worse, the Rails guide settled on double quotes
and a large part of the community also use rubocop which enforce
them by default.

So we might as well try to follow that style when providing code
snippets in the documentation or error messages.

Fix: https://github.com/rails/rails/issues/49822

I certainly didn't get them all, but consistency should be significantly
improved.
2023-10-28 11:38:49 +02:00
Jean Boussier
81ee4054da
Merge pull request #49776 from skipkayhil/hm-symbol-blank
Add Symbol#blank? to skip respond_to?(:empty?)
2023-10-28 11:02:51 +02:00
Jean Boussier
cb09616b94 Avoid exposing ActiveSupport::Testing::NoSkip
It's only meant for Rails internal use.
2023-10-27 09:03:30 +02:00
Jonathan Hefner
835d88bab9 Use h2 headings for Module::Concerning [ci-skip]
For SEO purposes, there should only be one `<h1>` per page, and the edge
version of SDoc will provide one using the module name if none are
present in documentation.
2023-10-26 17:37:32 -05:00
HolyWalley
f455d5be03 [Fix #49796] Prevent global cache options being overwritten
The reason of removing dup 5 years ago in 6da99b4e99 was to decrease memory allocations, so, it makes sense to only dup options when we know they will be overwritten.
2023-10-26 13:58:40 -05:00
Jonathan Hefner
87609e821b
Merge pull request #49791 from Earlopain/number-human-size-negative
Handle negative numbers in `NumberToHumanSizeConverter`
2023-10-26 11:20:59 -05:00
Earlopain
c6dcb11691
Handle negative numbers in NumberToHumanSizeConverter 2023-10-26 18:07:15 +02:00
Jean Boussier
9497f47131 Turn skips into errors on Rails CI
We had a few cases of tests being skipped accidentally on CI
hence not bein ran for a long time.

Skipping make sense when running the test suite locally, e.g.
you may not have Redis or some other dependency running.

But on CI, a test not being ran should be considered an error.
2023-10-26 12:46:01 +02:00
John Hawthorn
7beaacce51 Fix exception guards on multiple subscriber types
Previously in https://github.com/rails/rails/pull/43282, which shipped
with Rails 7.0, we added the guarantee that if an exception occurred
within an ActiveSupport::Notificaitons subscriber that the remaining
subscribers would still be run.

This was broken in https://github.com/rails/rails/pull/44469, where the
different types of subscribers were broken into groups by type for
performance. Although we would guard exceptions and allways run all (or
none) of the same group, that didn't work cross-group with different
types of subscriber.
2023-10-25 13:55:13 -07:00
Hartley McGuire
2dd3164c8c
Add Symbol#blank? to skip respond_to?(:empty?)
Any classes that don't implement #blank? will fall back to Object's
implementation, which checks whether #empty? is defined before either
using #empty? or implicit truthiness. Since checking whether #empty? is
defined is expensive, some core classes (Array/Hash) alias #blank?
directly to #empty? to skip the respond_to? check.

This commit applies the alias optimization to Symbol. #blank? is called
on Symbols for many Active Record query methods (select, includes,
group, order, joins, etc.) so it seems reasonable to define the #blank?
alias on Symbol as well.
2023-10-24 23:20:30 -04:00
Andrew Novoselac
c14b98c28a Fix BroadcastLogger#dup so that it duplicates the logger's broadcasts. 2023-10-23 18:12:28 -04:00
John Hawthorn
08473e3898
Merge pull request #49728 from jhawthorn/callbacks_sharing
Reduce memory used by ActiveSupport::Callbacks
2023-10-22 20:03:55 -07:00
yuuji.yaginuma
bbe591d8f4 Don't use deprecated #to_default_s in Date#to_fs
In Rails 7.1.1, using `Date#to_fs` with an un-exist format, shows the
deprecation warning like the following.

```ruby
Date.current.to_fs(:doesnt_exist)
=> DEPRECATION WARNING: to_default_s is deprecated and will be removed from Rails 7.2 (use to_s instead) (called from <main> at ./bin/rails:4)
```

But other date-time-related classes still allow to use this.

6f6c211f47/activesupport/lib/active_support/core_ext/time/conversions.rb (L57)
6f6c211f47/activesupport/lib/active_support/core_ext/date_time/conversions.rb (L39)

So I thought this was just a typo and fixed to use `#to_s` like
other classes.
2023-10-22 17:17:42 +09:00
John Hawthorn
578cdf8004 Avoid empty Arrays in ActiveSupport::Callbacks
ActiveSupport::Callbacks often ended up with empty Arrays: on callbacks
without a conditional, and on callback sequences which had no before
and/or after callbacks.
2023-10-21 06:51:03 -07:00
John Hawthorn
6c5a042824 Reduce memory used by ActiveSupport::Callbacks
Previously, callbacks which were shared with subclasses would not share
between them the procs generated in Filters::Before and Filters::After.
This was also lazily generated so would cause memory growth after an
application is booted (and not sharable between workers via CoW).

This was because we would rebuild the objects used to invoke the
callbacks via CallbackChain#compile, so any difference in the callback
chain would result in all of the callback procs being rebuilt.

This commit changes before and after callbacks (but not around!) to be
shared between all subclasses of where it was defined. This is done by
changing Filters::Before and Filters::After to plain classes which
respond to call instead of generating procs (which wasn't strictly
necessary but was easier to implement, and also results in simpler
objects which use less memory). These objects avoid referencing and tied
to a specific callback sequence and so can be memoized and reused.

This has the most impact on applications with many Controllers, and many
callbacks in the ApplicationController (or similar).

I also took this opportunity to merge together all the different forms
of procs generated (halting, halting_and_conditional, conditional,
simple) into one form with if statements. There isn't any significant
performance benefit from the specialization previously being done.
2023-10-20 18:02:33 -07:00
Jean Boussier
f11c6ac45c
Merge pull request #49716 from Shopify/invalid-compressed-cache-entries
Handle outdated Marshal payloads in Cache::Entry with 6.1 cache_format
2023-10-20 15:59:25 +02:00
Ryuta Kamizono
10d880dcd2
Merge pull request #49718 from fatkodima/fix-ordered_options-nested-dig
Fix `OrderedOptions#dig` for array indexes
2023-10-20 19:57:22 +09:00
fatkodima
1b211421cd Fix OrderedOptions#dig for array indexes 2023-10-20 13:44:33 +03:00
Jean Boussier
a6be798e5c Handle outdated Marshal payloads in Cache::Entry with 6.1 cache_format
Ref: https://github.com/rails/rails/issues/48611
Followup: https://github.com/rails/rails/pull/48663

It's the same logic than https://github.com/rails/rails/pull/48663
but now works for the 6.1 cache format.
2023-10-20 10:21:39 +02:00
fatkodima
7fd26579c0 Fix time travel helpers to work when nested using with separate classes 2023-10-20 02:46:42 +03:00
Jonathan Hefner
9f6b721b1d
Merge pull request #49694 from fatkodima/fix-file_store-key-splitting
Fix file cache store `delete_matched` to work on keys longer than allowed filename size
2023-10-19 13:48:01 -05:00
fatkodima
9e9fe7f391 Fix file cache store to split url-encoded keys on encode-sequence boundaries
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2023-10-19 21:33:02 +03:00
Jean Boussier
bcdeea5da7 Drop dependency on mutex_m
It used to be stdlib but is being extracted in modern rubies.

Overall its usefulness is dubious. In all cases it is included in
Rails, it's only for the `synchronize` method, but end up exposing
a dozen other useless methods.

In the end just using a Mutex is clearer and simpler.

In some cases we can even get away with a single mutex in a constant.
2023-10-18 14:27:26 +02:00
Jacopo
cebdf71a00 Fix decoding data encoded using a non-String purpose
Data encoded using a non-String purpose and `use_message_serializer_for_metadata == false` was incorrectly decoded,
triggering a "mismatched purpose" error during decode.
Fix this by ensuring that we compare both sides as a String.
2023-10-17 10:02:10 +02:00
Earlopain
1c7f9b0770
Better example for backtrace filter add_filter 2023-10-16 17:38:18 +02:00
zzak
93205a8805
Suppress mail warning for "unused variable - disp_type_s"
e.g.:

```
zzak@mbp16 railties % bin/test test/application/assets_test.rb
Run options: --seed 32028

.............../Users/zzak/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/mail-2.8.1/lib/mail/parsers/content_location_parser.rb:592: warning: assigned but unused variable - disp_type_s
I, [2023-10-15T10:38:33.005925 #97662]  INFO -- : [0ca46786-853f-4740-9e71-f644c4893502] Started GET "/assets/demo.js" for 127.0.0.1 at 2023-10-15 10:38:33 +0900
E, [2023-10-15T10:38:33.006641 #97662] ERROR -- : [0ca46786-853f-4740-9e71-f644c4893502]
[0ca46786-853f-4740-9e71-f644c4893502] ActionController::RoutingError (No route matches [GET] "/assets/demo.js"):
[0ca46786-853f-4740-9e71-f644c4893502]
../Users/zzak/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/mail-2.8.1/lib/mail/parsers/content_location_parser.rb:592: warning: assigned but unused variable - disp_type_s
..../Users/zzak/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/mail-2.8.1/lib/mail/parsers/content_location_parser.rb:592: warning: assigned but unused variable - disp_type_s
I, [2023-10-15T10:38:33.973335 #97660]  INFO -- : [fa848d82-0d80-4b23-925b-73fa5d0f47d4] Started GET "/posts?debug_assets=true" for 127.0.0.1 at 2023-10-15 10:38:33 +0900
I, [2023-10-15T10:38:33.975021 #97660]  INFO -- : [fa848d82-0d80-4b23-925b-73fa5d0f47d4] Processing by PostsController#index as HTML
I, [2023-10-15T10:38:33.975209 #97660]  INFO -- : [fa848d82-0d80-4b23-925b-73fa5d0f47d4]   Parameters: {"debug_assets"=>"true"}
I, [2023-10-15T10:38:33.977560 #97660]  INFO -- : [fa848d82-0d80-4b23-925b-73fa5d0f47d4] Completed 200 OK in 2ms (Views: 1.9ms | ActiveRecord: 0.0ms | Allocations: 1266)
......./Users/zzak/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/mail-2.8.1/lib/mail/parsers/content_location_parser.rb:592: warning: assigned but unused variable - disp_type_s
.

Finished in 4.842481s, 5.9887 runs/s, 23.3351 assertions/s.
29 runs, 113 assertions, 0 failures, 0 errors, 0 skips
```

For example:
https://buildkite.com/rails/rails/builds/94132#0186806f-8a23-4d07-ae8f-3b937f8517ae/1162-1171

See also #47484
2023-10-15 10:45:01 +09:00
Jean Boussier
e01d1e25dd ActiveSupport::LogSubscriber restore compatibility with SemanticLogger
Fix: https://github.com/rails/rails/pull/49563

The semantic_logger gems doesn't behave exactly like stdlib logger
in that `SemanticLogger#level` returns a Symbol while stdlib `Logger#level`
returns an Integer.

Because of this we can't simply compare integers, we have to use the
various `#{level}?` methods.
2023-10-13 14:21:23 +02:00
fatkodima
b8829cabec Enable Style/RedundantDoubleSplatHashBraces rubocop cop 2023-10-11 14:55:00 +03:00
Ryuta Kamizono
e8cad01402
Merge pull request #49576 from fatkodima/fix-number-helper-to_d
`NumberHelper`: handle objects responding `to_d`
2023-10-11 17:46:33 +09:00
fatkodima
fe3b07f683 NumberHelper: handle objects responding to_d 2023-10-11 01:38:21 +03:00
Jean Boussier
d4172bd44f
Merge pull request #49554 from Thomascountz/fix-redis-lt7-ttl-not-set-on-first-incr-decr
Fix `RedisCacheStore` INCR/DECR for Redis < v7.0.0
2023-10-11 00:15:16 +02:00
Jenny Shen
6070685cf9
Add support for kwargs when delegating calls to custom loggers
Currently, when a method is called on Rails.logger, the BroadcastLogger will find the loggers that will respond to that method. However, when the method has keyword arguments, they are passed as regular arguments and will throw an ArgumentError. This adds keyword argument support by double splatting hash args.

```
class CustomLogger
  def foo(bar:)
    true
  end
end

Rails.logger.foo(bar: "baz")
```

Expected: `true`

Actual: `wrong number of arguments (given 1, expected 0) (ArgumentError)`
2023-10-10 21:43:13 +01:00
Thomas Countz
600a052c8c Fix RedisCacheStore INCR/DECR for Redis < v7.0.0
This commit fixes a discrepancy in the behavior of the `#increment` and
`#decrement` methods in `RedisCacheStore` when used with Redis versions less
than 7.0.0. The existing condition `count != amount` prevented setting the
Time-To-Live (TTL) for keys that were equal to the increment/decrement amount
after the `INCRBY`/`DECRBY` operation. This occurs when incrementing a
non-existent key by `1`, for example.

Using Redis pipelining, we minimize the network overhead incurred by checking
for existing TTLs. It decouples the TTL operations from the increment/decrement
operation, allowing the TTL to be set correctly regardless of the resulting
value from the `INCRBY`/`DECRBY`.

New tests have been added to verify the correct behavior of `#increment` and
`#decrement` methods, specifically when the `expires_in` option is not used.
Using a separate cache store instance (`@cache_no_ttl`), these tests ensure that
keys are correctly incremented or decremented and that their TTL remains unset.

Co-authored-by: Benjamin Quorning <benjamin@quorning.net>
Co-authored-by: Jury Razumau <jury.razumau@zendesk.com>
Co-authored-by: Edyta Rozczypała <edyta.rozczypala@zendesk.com>
2023-10-10 19:32:26 +00:00
Jean Boussier
a385d7b170
Merge pull request #49542 from pjambet/pj/fix-memory-store-race-condition
MemoryStore: prevent race condition
2023-10-09 09:24:47 +02:00
Pierre Jambet
7f2e0ffb7e
MemoryStore: prevent race condition
It looks like #46305 accidentally removed the synchronize block that
would prevent a race conidition where two threads would read the same
value and only a single increment/decrement would take effect as they
would both write the same value.
2023-10-08 13:57:31 -04:00
Hartley McGuire
ec0600b7c7
Remove require of ActiveSupport::ForkTracker
ForkTracker has been autoloaded since [before][1] the [require][2] was
added.

[1]: 78b9580e5f3208c7048659de24f2220693afb23c
[2]: eba1534939fe1cf005746f12446235bdd52014c1
2023-10-07 23:29:02 -04:00
Jonathan Hefner
e5124aed3f Autolink references for AS::Notifications::Instrumenter [ci-skip]
This also rewords occurrences of "made since the call to `start!` and
the call to `finish!`" to "made between the call to `start!` and the
call to `finish!`", for clarity.
2023-10-07 12:26:33 -05:00
Jonathan Hefner
ed08eea99f Capitalize framework names [ci-skip] 2023-10-07 12:26:26 -05:00
Jonathan Hefner
4726b1ab47 Ensure on_rotation appears in RDoc [ci-skip]
When a `:method:` doc is immediately followed by the `private` keyword,
RDoc will hide that doc as if it were a private method.

To ensure that `ActiveSupport::MessageEncryptors#on_rotation` and
`ActiveSupport::MessageVerifiers#on_rotation` both appear in the
rendered docs, this commit adds a delimiter comment before each
`private` keyword.
2023-10-07 12:26:20 -05:00
Jonathan Hefner
dc99003487 Autolink AS::Notifications and AS::ErrorReporter [ci-skip] 2023-10-07 11:55:35 -05:00
Jonathan Hefner
d601d9d78a Fix typos for Cache::Store#fetch "Dynamic Options" [ci-skip] 2023-10-07 11:55:35 -05:00
Jonathan Hefner
46c42a2ff9 Omit list for Cache::Store#fetch "Dynamic Options" [ci-skip]
The extra indentation on this list causes it to be rendered as code
instead of as an unordered list.  But, furthermore, the items in this
list should be setter methods for `ActiveSupport::Cache::WriteOptions`,
not symbols.  Since the `ActiveSupport::Cache::WriteOptions` class is
linked in the preceding paragraph, we can simply omit this list.
2023-10-07 11:55:34 -05:00
Edouard CHIN
8be0c10c48 Add doc to explain how to migrate to the new BroadcastLogger:
- This should make it easier for apps or libraries that were
  previously relying on the private API.
  Also took the opportunity to tweak the doc of the BroadcastLogger
  to mention what happens when calling a non-standard method.

  Fix #49494
2023-10-07 14:43:46 +02:00
fatkodima
39438318c7 Implement HashWithIndifferentAccess#to_proc
Previously, calling `#to_proc` on `HashWithIndifferentAccess` object used inherited `#to_proc`
method from the `Hash` class, which was not able to access values using indifferent keys.

Fixes #48770.
2023-10-03 21:26:08 +03:00
Alex
ce321c4539 NumberHelper: handle very large numbers
Fixes https://github.com/rails/rails/issues/49461

Co-authored-by: fatkodima <5657035+fatkodima@users.noreply.github.com>
2023-10-03 14:46:06 +10:00
Eugene Kenny
5574a2fcb4 Delegate block in broadcast logger method_missing
When a method called on a broadcast logger is passed a block, it should
be forwarded to all subscribed loggers.
2023-10-02 16:35:01 +01:00
Jonathan Hefner
b68cc94096
Merge pull request #49447 from jonathanhefner/message_pack-fix-rails_max_threads
Fix `AS::MessagePack` with `ENV["RAILS_MAX_THREADS"]`
2023-10-01 15:51:43 -05:00
Jonathan Hefner
bb8ad695f4 Fix AS::MessagePack with ENV["RAILS_MAX_THREADS"]
`ENV` values are strings, so `ENV["RAILS_MAX_THREADS"]` must be parsed
as an int.

Unfortunately, `MessagePack::Factory::Pool::MemberPool` does not expose
a method to check its member count, so the most we can assert is that
roundtripping works as expected.

Fixes #49446.
2023-10-01 15:22:05 -05:00
Rafael Mendonça França
0f7fe4ab01
Revert "Also rescue TZInfo::ZoneinfoDirectoryNotFound when loading time zone data"
This reverts commit 54f30488e190eea5e923fe51914051df0e8c33f6.

Reason: THis isn't necessary. `TZInfo::DataSource.get` makes sure
the exception gets translated.
2023-10-01 20:20:06 +00:00
Rafael Mendonça França
db2ef1d250
Merge pull request #49417 from Edouard-chin/ec-logger-fix
Fix the BroadcastLogger being initialized too late:
2023-09-29 15:45:03 -04:00
Bart de Water
95b6fbd00f Stop building AS::Notifications::Event manually
It's possible since Rails 6 (3ea2857943dc294d7809930b4cc5b318b9c39577) to let the framework create Event objects, but the guides and docs weren't updated to lead with this example.

Manually instantiating an Event doesn't record CPU time and allocations, I've seen it more than once that people copy-pasting the example code get confused about these stats returning 0. The tests here show that - just like the apps I've worked on - the old pattern keeps getting copy-pasted.
2023-09-29 12:34:23 -04:00
Edouard CHIN
40cb50e06e Fix the BroadcastLogger being initialized too late:
- An oversight of #48615 is that it changes the `Rails.logger` to be
  a broadcast logger after the app is booted. Anything referencing
  `Rails.logger` during the boot process will get a simple logger and
  ultimately resulting in logs not being broadcasted.

  For example `ActionController::Base.logger.info("abc")` would
  just output logs in the `development.log` file, not on STDOUT.

  ----

  The only solution I could think of is to create a BroadcastLogger
  earlier at boot, and add logger to that broadcast when needed (instead
  of modiyfing the `Rails.logger` variable).
2023-09-29 15:42:47 +02:00
fatkodima
7ef86b6a49 Enable Lint/RedundantSafeNavigation rubocop cop 2023-09-27 14:55:07 +03:00
Rafael Mendonça França
fb6c6007d0
Development of Rails 7.2 starts now
🎉
2023-09-27 03:59:11 +00:00
Rafael Mendonça França
e5386cb402
Preparing for 7.1.0.rc1 release 2023-09-27 03:08:31 +00:00
Rafael Mendonça França
54f30488e1
Also rescue TZInfo::ZoneinfoDirectoryNotFound when loading time zone data
Fixes #49375.
2023-09-27 02:52:20 +00:00
Rafael Mendonça França
4c72cc2b04
Merge pull request #48615 from Edouard-chin/ec-logger
Add a public API for broadcasting logs
2023-09-25 17:13:58 -04:00
Edouard CHIN
1fbd812c47
Add a public API for broadcasting logs:
- ## Context

  While working on https://github.com/rails/rails/pull/44695, I
  realised that Broadcasting was still a private API, although it’s
  commonly used. Rafael mentioned that making it public would require
  some refactor because of the original implementation which was hard
  to understand and maintain.

  ### Changing how broadcasting works:

  Broadcasting in a nutshell worked by “transforming” an existing
  logger into a broadcasted one.
  The logger would then be responsible to log and format its own
  messages as well as passing the message along to other logger it
  broadcasts to.

  The problem with this approach was the following:

  - Heavy use of metaprogramming.
  - Accessing the loggers in the broadcast wasn’t possible.
    Removing a logger from the broadcast either.
  - More importantly, modifying the main logger (the one that broadcasts
    logs to the others) wasn’t possible and the main source of
    misunderstanding.

    ```ruby
      logger = Logger.new(STDOUT)
      stderr_logger = Logger.new(STDER))
      logger.extend(AS::Logger.broadcast(stderr_logger))

      logger.level = DEBUG # This modifies the level on all other loggers
      logger.formatter = … # Modified the formatter on all other loggers
    ```

  To keep the contract unchanged on what Rails.logger returns, the new
  BroadcastLogger class implement duck typing with all methods
  that has the vanilla Ruby Logger class.

  It's a simple and boring PORO that keeps an array of all the loggers
  that are part of the broadcast and iterate over whenever a log is
  sent.
  Now, users can access all loggers inside the broadcast and modify
  them on the fly. They can also remove any logger from the broadcast
  at any time.

  ```ruby
  # Before

  stdout_logger = Logger.new(STDOUT)
  stderr_logger = Logger.new(STDER)
  file_logger = Logger.new(“development.log”)
  stdout_logger.extend(AS::Logger.broadcast(stderr_logger))
  stdout_logger.extend(AS::Logger.broadcast(file_logger))

  # After

  broadcast = BroadcastLogger.new(stdout_logger, stderr_logger, file_logger)
  ```

  I also think that now, it should be more clear for users that the
  broadcast sole job is to pass everything to the whole loggers in
  the broadcast. So there should be no surprise that all loggers in
  the broadcast get their level modified when they call
  `broadcast.level = DEBUG` .

  It’s also easier to wrap your head around more complex setup such
  as broadcasting logs to another broadcast:
  `broadcast.broadcast_to(stdout_logger, other_broadcast)`
2023-09-25 20:40:51 +00:00
Rafael Mendonça França
71613d3da9
Merge pull request #45411 from jonathanhefner/add-deep_mergeable
Factor out `deep_merge` into `AS::DeepMergeable`
2023-09-25 16:25:44 -04:00
Hartley McGuire
0f5e7a6614
Update requires in LoggerThreadSafeLevel
The `concurrent` require was [added][1] previously because a
`Concurrent::Map` was added to hold all of the log levels by `Thread` id.
However, the `Map` was later [removed][2] by storing the log levels in the
`Thread` locals (and later in `IsolatedExecutionState`) instead. The new
implementation additionally removed the `cattr_accessor` (removing the
need to require the "attribute_accessors" core_ext) and also replaced
the [usage][3] of `Fiber` with `Thread` (so the require for `Fiber` is also no
longer necessary).

Since `Concurrent`, `Fiber`, and `cattr_accessor` are no longer used here, we
can remove the requires. Since `Logger` is used here (but was previously
required by `concurrent`), a require was added for it.

[1]: 629efb605728b31ad9644f6f0acaf3760b641a29
[2]: 2379bc5d2a7d9580f270eebfde87d9f94b3da6c9
[3]: 56ec504db6c130d448ffc1d68c9fdd95fdfc1130
2023-09-24 21:59:55 -04:00
Hartley McGuire
64184c6f50
Fix Range#overlap? ignoring empty ranges
Previously, #overlap? would incorrectly return true when one of the
ranges is effectively "empty":

```ruby
(2...2).overlap? 1..2 # => true
(1..2).overlap? 2...2 # => true
```

This is fixed in the Ruby 3.3 implementation of Range#overlap?, so this
commit fixes it for Ruby < 3.3 as well.

The tests added are from the Ruby repository and the implementation is
effectively a Ruby version of the fix in C.

Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
Co-authored-by: Shouichi Kamiya <shouichi.kamiya@gmail.com>
2023-09-22 19:48:49 -04:00
Hartley McGuire
b5dbcb1a3e
Add links to Callbacks, note example definition
Reading this doc left me confused where PersonRecord was defined, this
commit is intended to make it easier to find where it is.
2023-09-20 21:54:31 -04:00
Yasuo Honda
1d76d45411 Use Ruby 3.3 Range#overlap? if available
This commit uses Ruby 3.3 `Range#overlap?` that has been added to Ruby via https://github.com/ruby/ruby/pull/8242 .
Rails 7.1 renames `Range#overlaps?` to `Range#overlap?` via https://github.com/rails/rails/pull/48565 ,
This commit is not feasible to backport because there is no `Range#overlap?` in Rails 7.0.z

This commit addresses the CI faiilure at https://buildkite.com/rails/rails/builds/99745#018a9ea8-82f0-40a6-90c3-cdaa6dabebab/1092-1095
because without this commit, it shows `warning: method redefined; discarding old overlap?`.
```ruby
$ ruby -v
ruby 3.3.0dev (2023-09-16T05:57:19Z master e9b503f1bb) [x86_64-linux]
$ RAILS_STRICT_WARNING=true bundle exec ruby -w -Itest test/core_ext/range_ext_test.rb
/home/yahonda/src/github.com/rails/rails/activesupport/lib/active_support/core_ext/range/overlap.rb:7: warning: method redefined; discarding old overlap?
Running 46 tests in a single process (parallelization threshold is 50)
Run options: --seed 583

\# Running:

..............................................

Finished in 0.011672s, 3940.9670 runs/s, 4883.3722 assertions/s.
46 runs, 57 assertions, 0 failures, 0 errors, 0 skips
```
2023-09-19 19:47:35 +09:00
Jean Boussier
c15996c1bc Make ErrorReporterAssertions methods public
Since the methods are private they weren't documented.
2023-09-15 10:57:14 +02:00
Rafael Mendonça França
699dfdb426
Preparing for 7.1.0.beta1 release 2023-09-13 00:36:01 +00:00
Alex Ghiculescu
ff6881d2b2
Remove old raise_on_missing_translations behaviour
ref: https://github.com/rails/rails/pull/47105#issuecomment-1400843060

Removes the old `raise_on_missing_translations` accessors, that used to live [here](fee61e3abc/actionpack/lib/abstract_controller/translation.rb (L7)) and [here](5c835bd669/actionview/lib/action_view/helpers/translation_helper.rb (L15)).

Closes https://github.com/rails/rails/pull/45361
2023-09-09 19:59:49 +00:00
Rafael Mendonça França
fea9ad3337
Make sure the message format doesn't change
When `use_message_serializer_for_metadata` is false, the message
format should be enveloped in the same way it was in Rails 7.0
to make sure we don't have inconsistent formats.
2023-09-01 23:03:24 +00:00
Rafael Mendonça França
946550b467
Merge pull request #49067 from p8/performance/securerandom-choose
Use SecureRandom.alphanumeric for SecureRandom.base36/base58
2023-09-01 16:40:34 -04:00
Jean Boussier
553b44a044 Fix DescendantsTrackerTest when ran in isolation
Followup: https://github.com/rails/rails/pull/49108

DescendantsTracker need to work whether the `Class#descendants` core
ext is loaded or not. I missed that in the previous PR.
2023-09-01 18:19:40 +02:00
Jean Boussier
7407fe8a63 Remove useless include in DescendantsTracker
Either we are on a modern Ruby with `Class#subclass`, in which case
`DescendantsTracker#subclass` isn't defined, so we don't need the
filtering module.

Or we're on an old Ruby, and `DescendantsTracker.subclass` already does
the filtering.
2023-09-01 16:52:01 +02:00
Jean Boussier
a7fdc1cdb4 Optimize ActiveRecord::LogSubscriber#query_source_location
`Kernel#caller` has linear performance based on how deep the
stack is. While this is a development only feature, it can end
up being quite slow.

Ruby 3.2 introduced `Thread.each_caller_location`, which lazily
yield `Backtrace::Location` objects.

Ref: https://bugs.ruby-lang.org/issues/16663

This is perfect for this use case as we are searching for the
closest frame that matches a pattern, saving us from collecting
the entire backtrace.
2023-08-31 11:56:55 +02:00
Petrik
9aeffae14f Use SecureRandom.alphanumeric for SecureRandom.base36
Ruby 3.3 allows passing a list of characters to
`SecureRandom.alphanumeric`. For `SecureRandom.base36` using `choose` is
faster than the current implementation. For `SecureRandom.base58` it is
a bit slower.

I've also added a test to make sure passing nil as the length defaults
the length to 16.

_Benchmark__

```ruby

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails", github: "rails/rails", branch: "main"
  gem "benchmark-ips"
end

require "active_support"
require "active_support/core_ext/securerandom"

module SecureRandom
  def self.fast_base36(n)
    alphanumeric(n, chars: BASE36_ALPHABET)
  end
end

[10, 100, 1000, 10000].each do |length|
  puts
  puts " #{length} ".center(80, "=")
  puts

  Benchmark.ips do |x|
    x.report("base36")      { SecureRandom.base36(length) }
    x.report("fast_base36") { SecureRandom.fast_base36(length) }
    x.compare!
  end
end
```

```
====================================== 10 ======================================

Warming up --------------------------------------
              base36    20.513k i/100ms
         fast_base36    24.843k i/100ms
Calculating -------------------------------------
              base36    200.940k (±13.8%) i/s -    984.624k in   5.060203s
         fast_base36    235.531k (± 5.7%) i/s -      1.192M in   5.080574s

Comparison:
         fast_base36:   235530.9 i/s
              base36:   200939.9 i/s - same-ish: difference falls within error

===================================== 100 ======================================

Warming up --------------------------------------
              base36     2.746k i/100ms
         fast_base36     2.995k i/100ms
Calculating -------------------------------------
              base36     25.559k (± 8.5%) i/s -    129.062k in   5.087961s
         fast_base36     30.265k (± 6.6%) i/s -    152.745k in   5.070263s

Comparison:
         fast_base36:    30264.7 i/s
              base36:    25558.8 i/s - 1.18x  slower

===================================== 1000 =====================================

Warming up --------------------------------------
              base36   278.000  i/100ms
         fast_base36   308.000  i/100ms
Calculating -------------------------------------
              base36      2.595k (±11.6%) i/s -     12.788k in   5.007921s
         fast_base36      3.133k (± 6.1%) i/s -     15.708k in   5.033310s

Comparison:
         fast_base36:     3132.6 i/s
              base36:     2594.9 i/s - 1.21x  slower

==================================== 10000 =====================================

Warming up --------------------------------------
              base36    24.000  i/100ms
         fast_base36    34.000  i/100ms
Calculating -------------------------------------
              base36    256.601  (± 8.6%) i/s -      1.296k in   5.089604s
         fast_base36    322.119  (± 6.5%) i/s -      1.632k in   5.089614s

Comparison:
         fast_base36:      322.1 i/s
              base36:      256.6 i/s - 1.26x  slower

```
2023-08-30 11:28:42 +02:00
Matt Brictson
0bbaaf69ad
Remove outdated libxml-ruby version spec in XmlMini doc comment
XmlMini is currently being tested against libxml-ruby 4.0.0[^1], but the
doc comment for XmlMini says:

```
To use the much faster libxml parser:
gem 'libxml-ruby', '=0.9.7'
```

This comment seems to be very much out of date and could mislead users.

Fix by removing the version specifier so that the documentation simply
recommends:

```
To use the much faster libxml parser:
gem 'libxml-ruby'
XmlMini.backend = 'LibXML'
```

[^1]: 621bc68548/Gemfile.lock (L310)
2023-08-29 13:45:42 -07:00
Jean Boussier
6beb15c04a Add some :nodoc: to SyntaxErrorProxy and friends
None of this is public API.
2023-08-24 09:52:18 +02:00
Aaron Patterson
3338621efa
Use a temporary file for storing unencrypted files while editing
When we're editing the contents of encrypted files, we should use the
`Tempfile` class because it creates temporary files with restrictive
permissions.  This prevents other users on the same system from reading
the contents of those files while the user is editing them.

[CVE-2023-38037]
2023-08-22 10:23:15 -07:00
Jean Boussier
bef4c6904e
Merge pull request #48988 from jdelStrother/redis-distr-pipeline
Add Redis::Distributed support for pipelined write_multi
2023-08-21 15:30:53 +02:00
Jonathan del Strother
c47b4095cc
Add Redis::Distributed support for pipelined write_multi 2023-08-20 19:17:25 +01:00
Guillermo Iguaran
4ec3a986d5
Merge pull request #48959 from skipkayhil/hm-clean-filters-requires
Remove uneeded requires of core_ext/string/filters
2023-08-18 16:03:44 -07:00
Jonathan del Strother
1b3d884bb3
Add test coverage for RedisCacheStore with Redis::Distributed / ConnectionPool
This 'forward-ports' some tests added against 7-0-stable here: #48952

Also fixes a bug in supports_expire_nx? when using distributed-redis.
2023-08-17 12:34:28 +01:00
Hartley McGuire
ff6e885d59
Remove uneeded requires of core_ext/string/filters
`actionpack/lib/action_dispatch/routing.rb`
- added: 013745151be062aa4d0fc1f2a008a7303fdb6e04
- removed: 93034ad7fea7e00562103a7cd0acfab19bbfadf9

`activejob/lib/active_job/log_subscriber.rb`
- added: b314ab555e0d85e6efb41be94fb5f3a157bb12fe
- removed: 5ab2034730feacfc2caee418f8c0b55191d27427

`activemodel/lib/active_model/errors.rb`
- added: cf7fac7e29bb2816412c949fdaed3d61a923eb23
- removed: 9de6457ab0767ebab7f2c8bc583420fda072e2bd

`activerecord/lib/active_record/core.rb`
- added: b3bfa361c503e107aff4dee5edf79bd7fd3d3725
- removed: e1066f450d1a99c9a0b4d786b202e2ca82a4c3b3

`activesupport/lib/active_support/core_ext/module/introspection.rb`
- added: 358ac36edf1695fcbec0aa21f126a3d8b83d4b5a
- removed: 167b4153cac0069a21e0bb9689cb16f34f6abbaa

`activesupport/lib/active_support/duration.rb`
- added: 75924c4517c8f87712d3f59c11f10152ed57b9d8
- removed: a91ea1d51048342d13fc73f9b09ce4cfd086bb34

`railties/lib/rails/commands/server/server_command.rb`
- added: f2173648938b418d120f5a68d8f3862d8ae9dace
- removed: 553b86fc751c751db504bcbe2d033eb2bb5b6a0b

`railties/lib/rails/command/base.rb`
- added: 6813edc7d926965e5644cd8befaf229a35b9d8ca
- removed: b617a561d865a65cfc140caa0e3c4af4350bfcef
2023-08-16 17:39:25 -04:00
Jonathan Hefner
35b3db4945 Document and test nil cache coder
Since Rails 6.1 (via c4845aa7791839fcdf723dc77e3df258e7274496), it has
been possible to specify `coder: nil` to allow the store to handle cache
entries directly.

This commit adds documentation and regression tests for the behavior.
2023-08-06 13:03:52 -05:00
Rafael Mendonça França
5c69954d6b
Don't pring parallelization info when running in a single process 2023-08-05 02:32:12 +00:00
Rafael Mendonça França
5e3ca3b158
Set the variable to false instead of checking if it is defined
We already have an initalize method, so we can just set the variable to
false there.
2023-08-05 02:15:33 +00:00
Rafael Mendonça França
d1729d5332
Allow setting number of parallel workers to 1
In the case that one single test file can't run with more than 1
parallel workers, but the base class has parallelization enabled, we
should still allow the user to set the number of workers to 1.
2023-08-05 02:15:32 +00:00
John Hawthorn
6a5efd9777
Merge pull request #48669 from jhawthorn/faster_json_escape
Improve performance of JSON HTML entity escaping
2023-07-31 14:15:44 -07:00
Jonathan Hefner
36f9cfdd90 Lazily deserialize cache entries
This adds a cache optimization such that expired and version-mismatched
cache entries can be detected without deserializing their values.  This
optimization is enabled when using cache format version >= 7.1 or a
custom serializer.

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2023-07-27 14:58:35 -05:00
Petrik
3601a236dd Make all cache stores return a boolean for #delete
`Rails.cache.delete('key')` is supposed to return `true` if an entry
exists and `false` otherwise. This is how most stores behave.

However, the `RedisCacheStore` would return a `1` when deleting an entry
that does exist and a `0` otherwise.
As `0` is truthy this is unexpected behaviour.

`RedisCacheStore` now returns true if the entry exists and false
otherwise, making it consistent with the other cache stores.

Similarly the `FileCacheStore` now returns `false` instead of `nil` if
the entry doesn't exist.

A test is added to make sure this behaviour applies to all stores.

The documentation for `delete` has been updated to make the behaviour
explicit.
2023-07-27 08:48:30 +02:00
Jonathan Hefner
3efb84486e Support replacing cache compressor
This commit adds support for replacing the compressor used for
serialized cache entries.  Custom compressors must respond to `deflate`
and `inflate`.  For example:

  ```ruby
  module MyCompressor
    def self.deflate(string)
      # compression logic...
    end

    def self.inflate(compressed)
      # decompression logic...
    end
  end

  config.cache_store = :redis_cache_store, { compressor: MyCompressor }
  ```

As part of this work, cache stores now also support a `:serializer`
option.  Similar to the `:coder` option, serializers must respond to
`dump` and `load`. However, serializers are only responsible for
serializing a cached value, whereas coders are responsible for
serializing the entire `ActiveSupport::Cache::Entry` instance.
Additionally, the output from serializers can be automatically
compressed, whereas coders are responsible for their own compression.

Specifying a serializer instead of a coder also enables performance
optimizations, including the bare string optimization introduced by cache
format version 7.1.
2023-07-26 11:59:09 -05:00
Jonathan Hefner
3bdd57fba6 Support option aliases in RedisCacheStore#initialize
`ActiveSupport::Cache::UNIVERSAL_OPTIONS` already defines the list of
base options, and it includes option aliases such as `:expire_in` and
`:expired_in`.  Thus, using `UNIVERSAL_OPTIONS` allows `RedisCacheStore`
to support these aliases.
2023-07-25 15:57:21 -05:00
Jonathan Hefner
c3bf6bf38d Raise more specific error for cache format version
Follow-up to #48449.

Since #48449 changed the list of accepted cache format versions back to
just `6.1`, `7.0`, and `7.1`, we can raise a more specific error.
2023-07-25 15:57:21 -05:00
James Robinson
016b81f0b2 Fix ActiveSupport::Inflector.humanize(nil) 2023-07-25 17:00:28 +01:00
Étienne Barrié
b6ce10bcc6 Fix inconsistencies writing credentials values
Using [] or the dynamic accessors don't result in the same value because
`[]` is delegated to `config` (the decrypted deserialized YAML), whereas
`[]=` and the dynamic accessors are delegated to `options`, an
ActiveSupport::OrderedOptions instance.
2023-07-21 11:32:57 +02:00
Ufuk Kayserilioglu
c2b195e1e3
Change load error messages to use Kernel#warn instead of $stderr.puts
When development tools try to load Rails components, they sometimes end up loading files that will error out since a dependency is missing. In these cases, the tooling can catch the error and change its behaviour.

However, since the warning is printed directly to `$stderr`, the tooling cannot catch and suppress it easily, which ends up causing noise in the output of the tool.

This change makes Rails print these warnings using `Kernel#warn` instead, which can be suppressed by the tooling.
2023-07-21 00:38:12 +03:00
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