From c582817406e122fce7d5556f2bc64730b3ea9071 Mon Sep 17 00:00:00 2001 From: Elia Schito Date: Thu, 9 Jan 2020 17:02:57 +0100 Subject: [PATCH] Unload decorated classes before reloading decorators Loading decorators within `to_prepare` with `prepend` or `include` ends up adding multiple copies of the decorator to the ancestors of the decorated class. This typically causes trouble when using `super` within methods or when changes done in `prepended` or `included` are not idempotent. Clearing dependencies also solves problems related to overwriting constants and removed methods (i.e. when a method is removed by a decorator, previous instances of the decorator that are still in the ancestors list will still have that method defined and will intercept calls to it). --- .../spree/install/install_generator.rb | 5 ++++- .../developers/customizations/decorators.html.md | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/core/lib/generators/spree/install/install_generator.rb b/core/lib/generators/spree/install/install_generator.rb index f40cd91d76f..02d6a77ff75 100644 --- a/core/lib/generators/spree/install/install_generator.rb +++ b/core/lib/generators/spree/install/install_generator.rb @@ -83,8 +83,11 @@ def create_overrides_directory def configure_application application <<-RUBY - # Load application's model / class decorators initializer 'spree.decorators' do |app| + # Ensure decorated constants are unloaded before reloading decorators + config.to_prepare_blocks.unshift -> { ActiveSupport::Dependencies.clear } + + # Load application's decorators config.to_prepare do app.root.glob('app/**/*_decorator*.rb') { |path| require_dependency(path.to_s) } end diff --git a/guides/source/developers/customizations/decorators.html.md b/guides/source/developers/customizations/decorators.html.md index caa9778836e..d5452ae24dc 100644 --- a/guides/source/developers/customizations/decorators.html.md +++ b/guides/source/developers/customizations/decorators.html.md @@ -4,8 +4,22 @@ Solidus autoloads any file in the `/app` directory that has the suffix `_decorator.rb`, just like any other Rails models or controllers. This allows you to [monkey patch][monkey-patch] Solidus functionality for your store. +*Solidus install generator will add code such as this to `config/application.rb`* + +```ruby +initializer 'spree.decorators' do |app| + # Ensure decorated constants are unloaded before reloading decorators + config.to_prepare_blocks.unshift -> { ActiveSupport::Dependencies.clear } + + # Load application's decorators + config.to_prepare do + app.root.glob('app/**/*_decorator*.rb') { |path| require_dependency(path.to_s) } + end +end +``` + For example, if you want to add a method to the `Spree::Order` model, you could -create `/app/models/mystore/order_decorator.rb` with the following contents: +create `/app/models/my_store/order_decorator.rb` with the following contents: ```ruby module MyStore::OrderDecorator