Commit Graph

27 Commits

Author SHA1 Message Date
Petrik
ec28de4260 Add missing headers to Active Support docs [ci-skip]
Having a h1 heading will improve SEO and makes things look more consistent.
2023-04-23 16:02:56 +02:00
Rafael Mendonça França
711abd41c0
Extract values to constant 2023-01-16 20:41:15 +00:00
Alex Ghiculescu
1284df5286 ActiveSupport::CurrentAttributes: raise if a restricted attribute name is used.
Attributes such as `set` and `reset` should not be used as they clash with the  `CurrentAttributes` public API. This PR raises an `ArgumentError` if a restricted attribute name is used.
2023-01-15 17:07:11 -07:00
Alex Ghiculescu
be155f135a Support using a method name to define the ActiveSupport::CurrentAttributes.resets callback
Previously you could only use a block, which was inconsistent with other callback APIs in Rails. Now, you can use a block or a method name. The block API is still recommended in the docs.

```ruby
class Current < ActiveSupport::CurrentAttributes
  resets { Time.zone = nil }
  resets :clear_time_zone
end
```

This also works for `before_reset`.

```ruby
class Current < ActiveSupport::CurrentAttributes
  before_reset { Time.zone = nil }
  before_reset :clear_time_zone
end
```
2022-10-05 11:05:58 -05:00
Jean Boussier
540d2f41f6 Introduce ActiveSupport::IsolatedExecutionState for internal use
Many places in Active Support and Rails in general use `Thread.current#[]`
to store "request (or job) local data". This often cause problems with
`Enumerator` because it runs in a different fiber.

On the other hand, some places migrated to `Thread#thread_variable_get`
which cause issues with fiber based servers (`falcon`).

Based on this, I believe the isolation level should be an application
configuration.

For backward compatibility it could ship with `:fiber` isolation as a default
but longer term :thread would make more sense as it would work fine for
all deployment targets except falcon.

Ref: https://github.com/rails/rails/pull/38905
Ref: https://github.com/rails/rails/pull/39428
Ref: https://github.com/rails/rails/pull/34495
(and possibly many others)
2021-11-18 15:55:15 +01:00
Jean Boussier
bf33510d86 Optimize CurrentAttributes method generation
The bulk of the optimization is to generate code rather than use
`define_method` with a closure.

```
Warming up --------------------------------------
            original   207.468k i/100ms
      code-generator   340.849k i/100ms
Calculating -------------------------------------
            original      2.127M (± 1.1%) i/s -     10.788M in   5.073860s
      code-generator      3.426M (± 0.9%) i/s -     17.383M in   5.073965s

Comparison:
      code-generator:  3426241.0 i/s
            original:  2126539.2 i/s - 1.61x  (± 0.00) slower
```

```ruby

require 'benchmark/ips'
require 'active_support/all'

class Original < ActiveSupport::CurrentAttributes
  attribute :foo
end

class CodeGen < ActiveSupport::CurrentAttributes
  class << self
    def attribute(*names)
      ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
        names.each do |name|
          owner.define_cached_method(name, namespace: :current_attributes) do |batch|
            batch <<
              "def #{name}" <<
              "attributes[:#{name}]" <<
              "end"
          end
          owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch|
            batch <<
              "def #{name}=(value)" <<
              "attributes[:#{name}] = value" <<
              "end"
          end
        end
      end

      ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
        names.each do |name|
          owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
            batch <<
              "def #{name}" <<
              "instance.#{name}" <<
              "end"
          end
          owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
            batch <<
              "def #{name}=(value)" <<
              "instance.#{name} = value" <<
              "end"
          end
        end
      end
    end
  end
  attribute :foo
end

Benchmark.ips do |x|
  x.report('original') { Original.foo }
  x.report('code-generator') { CodeGen.foo }
  x.compare!
end
```
2021-11-02 15:52:25 +01:00
Étienne Barrié
39b382cf78 Remove leak when using CurrentAttributes in thread variables
The test for CurrentAttributes using thread-local variables leaks
an instance in Thread.current.

Usually instances are reset in the test helper but the objects remain:
activesupport/lib/active_support/current_attributes/test_helper.rb:11

In this situation we use clear_all to make sure we don't leave instances
behind when the configuration is changed.
2021-07-27 11:50:37 +02:00
Étienne Barrié
8195cd5f99 Allow using thread variables for CurrentAttributes instances 2021-07-21 16:53:32 +02:00
Rafael Mendonça França
4354e3ae49
Don't define methods using the method modifier in the same line as the method
Our style guide use block method modifiers, not inline method modifiers.
2021-04-12 18:49:54 +00:00
Jean Boussier
3f59640016 Stop checking if ruby2_keywords is defined 2021-04-11 13:42:02 +02:00
Ufuk Kayserilioglu
302fdb7677
Add missing require 2021-03-03 15:48:20 -05:00
Ryuta Kamizono
7b9cfde741 Fix method_missing delegation to not expand positional hash argument
Follow up to #41518, and similar to 3a85ced1a03c3e4fa81e316d06a65a75c760cf8c.
2021-03-02 22:29:29 +09:00
Marcin Kolodziej
9d6b2b38fd Fix proxying keyword arguments for ActiveSupport::CurrentAttributes.
Also fixes `respond_to?` not working for methods that have not yet been delegated.
2021-02-22 17:42:48 +01:00
Eugene Kenny
d3599d8aff Use index_by and index_with wherever possible
Using `index_by` or `index_with` is more concise than `each_with_object`
and more performant than `map { ... }.to_h` or `Hash[map { ... }]`.
2020-03-05 01:24:14 +00:00
Dylan Thacker-Smith
65cd16b541 activesupport: Memoize name object allocation in CurrentAttributes.instance
Also, use a symbol key for a faster hash lookup.
2019-08-19 13:05:56 -04:00
Rosa Gutierrez
2ce8455cc9 Support before_reset callback in CurrentAttributes
This is useful when we need to do some work associated to `Current.reset`
but that work depends on the values of the current attributes themselves.
This cannot be done in the supported `resets` callback because when the
block is executed, CurrentAttributes's instance has already been reset.

For symmetry, `after_reset` is defined as alias of `resets`.
2019-01-30 16:10:06 +01:00
Akira Matsuda
5ab2505084 Missing require "active_support/callbacks" 2018-10-20 14:58:04 +09:00
Claudio B
af954ddd54 [ci skip] Prefer cookies.encrypted over signed (#30129)
In some examples and guides we are recommending to use code like:

```ruby
verified_user = User.find_by(id: cookies.signed[:user_id])
```

My suggestion is to use instead:

```ruby
verified_user = User.find_by(id: cookies.encrypted[:user_id])
```

which invites users to prefer the "newer" encrypted cookies over the
"legacy" signed cookies.
2017-08-07 22:32:03 -05:00
Koichi ITO
ac717d65a3 [Active Support] rubocop -a --only Layout/EmptyLineAfterMagicComment 2017-07-11 13:12:32 +09:00
Kir Shatrov
72950568dd Use frozen-string-literal in ActiveSupport 2017-07-09 15:08:29 +03:00
Matthew Draper
87b3e226d6 Revert "Merge pull request #29540 from kirs/rubocop-frozen-string"
This reverts commit 3420a14590c0e6915d8b6c242887f74adb4120f9, reversing
changes made to afb66a5a598ce4ac74ad84b125a5abf046dcf5aa.
2017-07-02 02:15:17 +09:30
Kir Shatrov
cfade1ec7e Enforce frozen string in Rubocop 2017-07-01 02:11:03 +03:00
Kasper Timm Hansen
85211ea1ef Clear all current instances before a reload.
If users added an attribute or otherwise changed a CurrentAttributes subclass
they'd see exceptions on the next page load.

Because `ActiveSupport::CurrentAttributes.current_instances` would keep
references to the old instances from the previous request.

We can fix this by clearing out the `current_attributes` before we unload
constants. Then any change to the model can be autoloaded again since its
slot isn't taken by an old instance.

We'll still have to call reset before we clear so external collaborators,
like Time.zone, won't linger with their current value throughout other code.
2017-05-28 10:47:17 +02:00
Kasper Timm Hansen
3cacbc1ef0 Remove double Thread.current storage.
Since we're generating a key through the class name we can combine
the two Thread.current calls into a single hash version.
2017-05-28 10:17:36 +02:00
Kasper Timm Hansen
fcc47bcfcc Use non-raising finder.
`find` raises when it can't find a record, so we'll never reach the
else. Switch to `find_by` which returns nil when no record can be
found.
2017-05-27 14:36:18 +02:00
Kasper Timm Hansen
3a131b6c00 [ci skip] Fix spelling that's a bit of an overreach. 2017-05-27 14:34:53 +02:00
David Heinemeier Hansson
24a864437e ActiveSupport::CurrentAttributes provides a thread-isolated attributes singleton (#29180)
* Add ActiveSupport::CurrentAttributes to provide a thread-isolated attributes singleton

* Need to require first

* Move stubs into test namespace.

Thus they won't conflict with other Current and Person stubs.

* End of the line for you, whitespace!

* Support super in attribute methods.

Define instance level accessors in an included module such that
`super` in an overriden accessor works, akin to Active Model.

* Spare users the manual require.

Follow the example of concerns, autoload in the top level Active Support file.

* Add bidelegation support

* Rename #expose to #set. Simpler, clearer

* Automatically reset every instance.

Skips the need for users to actively embed something that resets
their CurrentAttributes instances.

* Fix test name; add tangible name value when blank.

* Try to ensure we run after a request as well.

* Delegate all missing methods to the instance

This allows regular `delegate` to serve, so we don't need bidelegate.

* Properly test resetting after execution cycle.

Also remove the stale puts debugging.

* Update documentation to match new autoreset
2017-05-26 20:00:27 +02:00