From 22b31931b179584de4f61634d08ee0ad0ce1a1a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 13:00:50 +0000 Subject: [PATCH 1/4] Initial plan From b4473db8c4b03723cba8a8d148bc9e4664eebf19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 13:08:33 +0000 Subject: [PATCH 2/4] Add const_missing to delegate missing constants to object class Co-authored-by: Alexander-Senko <1022687+Alexander-Senko@users.noreply.github.com> --- lib/draper/automatic_delegation.rb | 7 +++++++ spec/draper/decorator_spec.rb | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/draper/automatic_delegation.rb b/lib/draper/automatic_delegation.rb index 0546f4c4..b7aca0b9 100644 --- a/lib/draper/automatic_delegation.rb +++ b/lib/draper/automatic_delegation.rb @@ -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? + + super + end + # @private def delegatable?(method) object_class? && object_class.respond_to?(method) diff --git a/spec/draper/decorator_spec.rb b/spec/draper/decorator_spec.rb index 5536c696..4bf181cd 100644 --- a/spec/draper/decorator_spec.rb +++ b/spec/draper/decorator_spec.rb @@ -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} From a2423d2c21aac71971f51d9fdde9637e1b35fd4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 13:16:16 +0000 Subject: [PATCH 3/4] Update README and CHANGELOG for const_missing constant delegation Co-authored-by: Alexander-Senko <1022687+Alexander-Senko@users.noreply.github.com> --- CHANGELOG.md | 5 +++++ README.md | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4449c35..0934f409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 588434b3..271afc47 100644 --- a/README.md +++ b/README.md @@ -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: From 04bd5726373d94556e26a742e8aa1e1372a3f9a8 Mon Sep 17 00:00:00 2001 From: Alexander Senko Date: Sat, 28 Feb 2026 18:07:01 +0700 Subject: [PATCH 4/4] Update lib/draper/automatic_delegation.rb Co-authored-by: y-yagi --- lib/draper/automatic_delegation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/draper/automatic_delegation.rb b/lib/draper/automatic_delegation.rb index b7aca0b9..97f2310f 100644 --- a/lib/draper/automatic_delegation.rb +++ b/lib/draper/automatic_delegation.rb @@ -51,7 +51,7 @@ def respond_to_missing?(method, include_private = false) # Proxies missing constants to the source class. def const_missing(name) - return object_class.const_get(name) if object_class? + return object_class.const_get(name) if object_class? && object_class.const_defined?(name) super end