Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Draper Changelog

## Unreleased

### New Features
* Delegate missing constants to the object class via `const_missing` when using `delegate_all` [#955](https://github.com/drapergem/draper/pull/955)

## 4.0.6 - 2025-11-15

### Fixes
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,21 @@ the decorator will first try to call the method on the parent decorator class. I
the method does not exist on the parent decorator class, it will then try to call
the method on the decorated `object`. This is a very permissive interface.

Constants defined on the source model class are also accessible via the decorator
class when using `delegate_all`:

```ruby
class Car
WHEELS_COUNT = 4
end

class CarDecorator < ApplicationDecorator
delegate_all
end

CarDecorator::WHEELS_COUNT # => 4
```

If you want to strictly control which methods are called within views, you can
choose to only delegate certain methods from the decorator to the source model:

Expand Down
7 changes: 7 additions & 0 deletions lib/draper/automatic_delegation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ def respond_to_missing?(method, include_private = false)
super || delegatable?(method)
end

# Proxies missing constants to the source class.
def const_missing(name)
return object_class.const_get(name) if object_class?
Comment thread
Alexander-Senko marked this conversation as resolved.
Outdated

super
end

# @private
def delegatable?(method)
object_class? && object_class.respond_to?(method)
Expand Down
24 changes: 24 additions & 0 deletions spec/draper/decorator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,30 @@ def hello_world
end
end

context ".const_missing" do
context "without an object class" do
it "raises a NameError on missing constants" do
expect{Decorator::HELLO_WORLD}.to raise_error NameError, /HELLO_WORLD/
end
end

context "with an object class" do
it "delegates constants that exist on the object class" do
object_class = Class.new
object_class.const_set(:HELLO_WORLD, :delegated)
allow(Decorator).to receive_messages object_class: object_class

expect(Decorator::HELLO_WORLD).to be :delegated
end

it "raises a NameError for constants that do not exist on the object class" do
allow(Decorator).to receive_messages object_class: Class.new

expect{Decorator::HELLO_WORLD}.to raise_error NameError, /HELLO_WORLD/
end
end
end

describe "#respond_to?" do
it "returns true for its own methods" do
Decorator.class_eval{def hello_world; end}
Expand Down