Skip to content

Commit

Permalink
Unload decorated classes before reloading decorators
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
elia committed Jan 9, 2020
1 parent 3eba2ef commit c582817
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 2 deletions.
5 changes: 4 additions & 1 deletion core/lib/generators/spree/install/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion guides/source/developers/customizations/decorators.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c582817

Please sign in to comment.