Commit Graph

7747 Commits

Author SHA1 Message Date
Jean Boussier
946e46ebcc Modernize method missing implementations
`...` is both simpler an more correct since the keyword argument
separation.
2024-01-16 13:17:45 +01:00
r-plus
38151711c8 Fix IPAddr prefix information missing when write to cache in msgpack serializer
* Save cache size by omit the prefix if unnecessary

* rename to straightforward naming.

* check the prefix directly instead of inspect

* Remove unused helper method

* add to changelog

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2024-01-15 00:40:44 -06:00
Jonathan Hefner
aa98bc3c71 Prevent CurrentAttributes defaults from leaking
Follow-up to #50677.

Prior to this commit, all `ActiveSupport::CurrentAttributes` subclasses
stored their default values in the same `Hash`, causing default values
to leak between classes.  This commit ensures each subclass maintains a
separate `Hash`.

This commit also simplifies the resolution of default values, replacing
the `merge_defaults!` method with `resolve_defaults`.
2024-01-11 12:40:14 -06:00
Jean Boussier
4bded3c00a Module#delegate avoid creating a unique fstring for each delegator
For example:

```ruby
delegate :negative?, to: :value, as: Numeric
```

Before:

```
def negative?(&block)
  _ = @value
  _.negative?(&block)
rescue NoMethodError => e
  if _.nil? && e.name == :negative?
    raise DelegationError, "ActiveSupport::Duration#negative? delegated to @value.negative?, but @value is nil: #{self.inspect}"
  else
    raise
  end
end
```

After:

```ruby
def negative?(&block)
  _ = @value
  _.negative?(&block)
rescue NoMethodError => e
  if _.nil? && e.name == :negative?
    raise DelegationError.nil_target(:negative?, :"@value")
  else
    raise
  end
end
```

Before almost every delegator would generate a large unique string that gets interned for
the error message that is rarely if ever used.

Rather than to "hardcode" a unique string, we now only pass pre-existing symbols to
a method helper that will build the error message.

This alone saves about 160B per delegator, and the method bytecode is also marginally
smaller (but it's harder to assess how much this actually saves)
2024-01-10 19:27:50 +01:00
Jean Boussier
61b48fe76d
Merge pull request #50686 from seanpdoyle/remove-current-attributes-method-missing
Avoid definition of methods at runtime in `CurrentAttributes`
2024-01-10 09:40:09 +01:00
Sean Doyle
c8e5b0b531 Avoid definition of methods in CurrentAttributes at runtime
Replacing on the fly a `method_missing` by a generated method
sound like a nice trick, but it's not as good as it sound for
optimization, as the method will be generated by the first
request to use it, preventing the ISeq from being is shared memory.

Instead we can eagerly define a delegator when instance methods
are defined, and keep a regular `method_missing + send` for the
very rare cases not covered.

Co-Authored-By: Jean Boussier <jean.boussier@gmail.com>
2024-01-10 08:59:35 +01:00
Sean Doyle
d1d6b6bce3 Add default: support for ActiveSupport::CurrentAttributes.attribute
Extend the `.attribute` class method to accept a `:default` option for
its list of attributes:

```ruby
class Current < ActiveSupport::CurrentAttributes
  attribute :counter, default: 0
end
```

Internally, `ActiveSupport::CurrentAttributes` will maintain a
`.defaults` class attribute to determine default values during instance
initialization.
2024-01-09 19:09:57 -05:00
Sean Doyle
2cd4abcc87 Simplify CurrentAttribute.instance delegation
Follow-up to [#50676][]

Instead of relying on code generation, call a corresponding [delegate][]
method on the `.singleton_class`.

[#50676]: https://github.com/rails/rails/pull/50676
[delegate]: https://edgeapi.rubyonrails.org/classes/Module.html#method-i-delegate

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2024-01-09 16:54:00 -05:00
Sean Doyle
3c72983dc5 Implement CurrentAttributes#set in terms of Object#with
`CurrentAttributes` supports block-scoped overrides for its attributes
through the `#set` method. The introduction of [CurrentAttributes#set][]
predates the introduction of [Object#with][] by 6 years.

This commit changes the implementation of `#set` to delegate to `#with`.
Through that delegation, the private `#assign_attributes` and
`#compute_attributes` methods are no longer necessary.

[CurrentAttributes#set]: 2d6b02bad6/activesupport/lib/active_support/current_attributes.rb (L210)
[Object#with]: 2d6b02bad6/activesupport/lib/active_support/core_ext/object/with.rb (L26)
2024-01-09 14:34:18 -05:00
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