Commit Graph

7754 Commits

Author SHA1 Message Date
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