Commit Graph

7899 Commits

Author SHA1 Message Date
Jean Boussier
a3d05309aa Get rid of ForkTracker.check!
Now that we require Ruby 3.1, we can assume `Process._fork` is
defined on MRI, hence we can trust that our decorator will
reliably detect forks so we no longer need to check the if
the pid changed in critical spots.
2024-01-09 11:18:38 +01:00
Jean Boussier
8c7e69b79b Optimize Hash#stringify_keys
Using Symbol#name allows to hit two birds with one stone.

First it will return a pre-existing string, so will save
one allocation per key.

Second, that string will be already interned, so it will
save the internal `Hash` implementation the work of looking
up the interned strings table to deduplicate the key.

```
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin21]
Warming up --------------------------------------
                to_s    17.768k i/100ms
                cond    23.703k i/100ms
Calculating -------------------------------------
                to_s    169.830k (±10.4%) i/s -    852.864k in   5.088377s
                cond    236.803k (± 7.9%) i/s -      1.185M in   5.040945s

Comparison:
                to_s:   169830.3 i/s
                cond:   236803.4 i/s - 1.39x  faster
```

```ruby
require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'benchmark-ips', require: false
end

HASH = {
  first_name: nil,
  last_name: nil,
  country: nil,
  profession: nil,
  language: nil,
  hobby: nil,
  pet: nil,
  longer_name: nil,
  occupation: nil,
  mailing_address: nil,
}.freeze

require 'benchmark/ips'

Benchmark.ips do |x|
  x.report("to_s") { HASH.transform_keys(&:to_s) }
  x.report("cond") { HASH.transform_keys { |k| Symbol === k ? k.name : k.to_s } }
  x.compare!(order: :baseline)
end
```
2024-01-08 19:19:20 +01:00
Earlopain
d96c424fab
Remove core_ext/uri.rb exception
The file was removed in da8e6f6175
2024-01-08 10:58:39 +01:00
Jonathan Hefner
3bbf21c343 Use verb form of "fallback"
"Fallback" is a noun, whereas "fall back" is a verb.
2024-01-07 17:27:23 -06:00
Jean Boussier
c0b5052d92
Merge pull request #50609 from ricardotk002/use-array-intersect
Replace usage of `Array#?` with `Array#intersect?` for efficiency
2024-01-07 21:15:56 +01:00
Sean Doyle
9e64b13d8a Yield instance to Object#with block
The introduction of the block argument means that `Object#with` can now
accept a `Symbol#to_proc` as the block argument:

```ruby
client.with(timeout: 5_000) do |c|
  c.get("/commits")
end
```
2024-01-07 20:56:33 +01:00
Jonathan Hefner
53ba75d1aa Clean up AS::NumberHelper#number_to_human doc [ci-skip] 2024-01-06 18:07:26 -06:00
Jonathan Hefner
2b801dcec4 Clean up AS::NumberHelper#number_to_human_size doc [ci-skip] 2024-01-06 18:07:26 -06:00
Jonathan Hefner
8a04207991 Clean up AS::NumberHelper#number_to_rounded doc [ci-skip] 2024-01-06 18:07:26 -06:00
Jonathan Hefner
a0e7aaa085 Clean up AS::NumberHelper#number_to_delimited doc [ci-skip] 2024-01-06 18:07:26 -06:00
Jonathan Hefner
1d02d9b472 Clean up AS::NumberHelper#number_to_percentage doc [ci-skip] 2024-01-06 18:07:26 -06:00
Jonathan Hefner
9acf9e6e49 Clean up AS::NumberHelper#number_to_currency doc [ci-skip] 2024-01-06 18:07:26 -06:00
Jonathan Hefner
6be13c827a Clean up AS::NumberHelper#number_to_phone doc [ci-skip] 2024-01-06 18:07:26 -06:00
Ricardo Díaz
de154095ed Replace usage of Array#? with Array#intersect? for efficiency
`Array#intersect?` was introduced in Ruby 3.1.0 and it's more efficient and
useful when the result of the intersection is not needed as the
following benchmarks show:

```
require "bundler/inline"

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

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

  gem "rails", path: "./"
  # If you want to test against edge Rails replace the previous line with this:
  # gem "rails", github: "rails/rails", branch: "main"
  gem "benchmark-ips"
end

require "active_support"

SCENARIOS = [
  [(1..100).to_a, (90..200).to_a],    # Case 1
  [("a".."m").to_a, ("j".."z").to_a], # Case 2
  [(1..100).to_a, (101..200).to_a],   # Case 3
]

SCENARIOS.each_with_index do |values, n|
  puts
  puts " Case #{n + 1} ".center(80, "=")
  puts
  Benchmark.ips do |x|
    x.report("Array#?") { !(values[0] & values[1]).empty? }
    x.report("Array#intersect?")      { values[0].intersect?(values[1]) }
    x.compare!
  end
end
```

Results:

```
==================================== Case 1 ====================================

ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin21]
Warming up --------------------------------------
             Array#?    34.221k i/100ms
    Array#intersect?    62.035k i/100ms
Calculating -------------------------------------
             Array#?    343.119k (± 1.1%) i/s -      1.745M in   5.087078s
    Array#intersect?    615.394k (± 1.1%) i/s -      3.102M in   5.040838s

Comparison:
    Array#intersect?:   615393.7 i/s
             Array#?:   343119.4 i/s - 1.79x  slower

==================================== Case 2 ====================================

ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin21]
Warming up --------------------------------------
             Array#?   103.256k i/100ms
    Array#intersect?   185.104k i/100ms
Calculating -------------------------------------
             Array#?      1.039M (± 1.3%) i/s -      5.266M in   5.066847s
    Array#intersect?      1.873M (± 1.6%) i/s -      9.440M in   5.041740s

Comparison:
    Array#intersect?:  1872932.7 i/s
             Array#?:  1039482.4 i/s - 1.80x  slower

==================================== Case 3 ====================================

ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin21]
Warming up --------------------------------------
             Array#?    37.070k i/100ms
    Array#intersect?    41.438k i/100ms
Calculating -------------------------------------
             Array#?    370.902k (± 0.8%) i/s -      1.891M in   5.097584s
    Array#intersect?    409.902k (± 1.0%) i/s -      2.072M in   5.055185s

Comparison:
    Array#intersect?:   409901.8 i/s
             Array#?:   370902.3 i/s - 1.11x  slower
```
2024-01-05 15:15:30 -05:00
Jean Boussier
21090a1319
Merge pull request #50602 from Shopify/symbol-name-cleanup
Update shims for older rubies
2024-01-05 17:22:23 +01:00
Jean Boussier
6c357e9571 Update shims for older rubies
`Symbol#name` and `Time#floor` are now present on the minimum required version.
2024-01-05 17:04:05 +01:00
Rafael Mendonça França
4760e07c1e
Merge pull request #50590 from sato11/cache-can-drop-ruby-2-7-compatibility
Remove workaround for Ruby 2.7 at ActiveSupport::Cache#lookup_store
2024-01-05 10:03:24 -05:00
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