Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions activesupport/lib/active_support/current_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ def attribute(*names, default: NOT_SET)
raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
end

Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")

ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
names.each do |name|
owner.define_cached_method(name, namespace: :current_attributes) do |batch|
Expand All @@ -134,9 +137,6 @@ def attribute(*names, default: NOT_SET)
end
end

Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")

self.defaults = defaults.merge(names.index_with { default })
end

Expand Down Expand Up @@ -185,9 +185,16 @@ def respond_to_missing?(name, _)

def method_added(name)
super

# We try to generate instance delegators early to not rely on method_missing.
return if name == :initialize

# If the added method isn't public, we don't delegate it.
return unless public_method_defined?(name)

# If we already have a class method by that name, we don't override it.
return if singleton_class.method_defined?(name) || singleton_class.private_method_defined?(name)

Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
end
end
Expand Down
31 changes: 31 additions & 0 deletions activesupport/test/current_attributes_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,35 @@ def foo; end # Sets the cache because of a `method_added` hook

assert_instance_of(Hash, current.bar)
end

test "instance delegators are eagerly defined" do
current = Class.new(ActiveSupport::CurrentAttributes) do
def self.name
"MyCurrent"
end

def regular
:regular
end

attribute :attr, default: :att
end

assert current.singleton_class.method_defined?(:attr)
assert current.singleton_class.method_defined?(:attr=)
assert current.singleton_class.method_defined?(:regular)
end

test "attribute delegators have precise signature" do
current = Class.new(ActiveSupport::CurrentAttributes) do
def self.name
"MyCurrent"
end

attribute :attr, default: :att
end

assert_equal [], current.method(:attr).parameters
assert_equal [[:req, :value]], current.method(:attr=).parameters
end
end