-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Stop generating code to autoload overrides
Up to this PR, when installing Solidus, we're adding code to the host application to load files matching `app/**/*_decorator*.rb`. There exist a series of issues about that: - It goes against current recommendations on the Rails guides, where overrides are placed under `app/overrides/**/_*override.rb` ( see https://guides.rubyonrails.org/engines.html#improving-engine-functionality). - The `_decorator.rb` suffix is misleading, as that kind of overrides has to be seen as monkey patches and not related to the decorator pattern (see https://en.wikipedia.org/wiki/Decorator_pattern). - Although it's something needed a lot of times, monkey patching core classes should be seen as a last resort after ruling out other strategies (like configuring a custom class for some behavior). As such, we should not encourage it. - It may conflict with the naming used by other gems, like Draper (https://github.com/drapergem/draper). For all of that, it's something that we should address in the docs. This change is backward compatible, as the running code doesn't belong to the Solidus engine but to the previously generated applications, which will keep it after upgrading. The deleted code is, in fact, intended to work with the classic autoloader (removed on Rails 7). However, it still works because of https://github.com/rails/rails/blob/296ef7a17221e81881e38b51aa2b014d7a28bac5/activesupport/lib/active_support/dependencies/require_dependency.rb, which is deprecated. We add a notice to the CHANGELOG to let users known about that. On a related information, the recommended `app/overrides/` directory is the same used by `deface` (https://github.com/spree/deface), a common dependency in Solidus projects. However, both types of overrides can coexist without interfering with each other (see #3010 (comment)). We decided to tackle the issue upstream on `deface` (see #3010 (comment)). Closes #3010
- Loading branch information
1 parent
d467123
commit 56bc7fc
Showing
5 changed files
with
143 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
guides/source/developers/customizations/monkey_patches.html.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# Monkey Patches | ||
|
||
> If you're using a Solidus version minor than 3.2, the content on this page is | ||
> still applicable. However, you might want to look at the previously recommended | ||
> approach through [decorators][decorators]. | ||
You can take advantage of Ruby's meta-programming features to [monkey | ||
patch][monkey-patch] Solidus functionality for your store. | ||
|
||
As the first thing, you need to configure a directory where you'll place your | ||
custom code. For instance, `app/overrides/`: | ||
|
||
```ruby | ||
# config/application.rb | ||
module MyStore | ||
class Application < Rails::Application | ||
# ... | ||
overrides = "#{Rails.root}/app/overrides" | ||
Rails.autoloaders.main.ignore(overrides) | ||
config.to_prepare do | ||
Dir.glob("#{overrides}/**/*.rb").each do |override| | ||
load override | ||
end | ||
end | ||
end | ||
end | ||
``` | ||
|
||
> If you're using the classic autoloader (the default before Rails 6), you | ||
instead need to go with: | ||
> | ||
> ```ruby | ||
> # config/application.rb | ||
> module MyStore | ||
> class Application < Rails::Application | ||
> # ... | ||
> config.to_prepare do | ||
> Dir.glob("#{Rails.root}/app/overrides/**/*.rb").each do |override| | ||
> require_dependency override | ||
> end | ||
> end | ||
> end | ||
> end | ||
> ``` | ||
For example, if you want to add a method to the `Spree::Order` model, you could | ||
create `/app/overrides/my_store/order_total_modifier.rb` with the following contents: | ||
```ruby | ||
module MyStore::OrderTotalModifier | ||
def total | ||
super + BigDecimal(10.0) | ||
end | ||
Spree::Order.prepend self | ||
end | ||
``` | ||
This creates a new module called `MyStore::OrderTotalModifier` that prepends | ||
its methods early in the method lookup chain. So, for method calls on | ||
`Spree::Order` objects, the monkey patch's `total` method would override the | ||
original `total` method. | ||
|
||
With the code above live on your server, every call to `Spree::Order.total` will | ||
return the original total plus $10 (or whatever your currency is). | ||
|
||
[monkey-patch]: https://en.wikipedia.org/wiki/Monkey_patch | ||
[decorators]: decorators.html | ||
|
||
## Using class-level methods | ||
|
||
You'll need to define a special method in order to access some class-level | ||
methods | ||
|
||
```ruby | ||
module MyStore::ProductAdditions | ||
|
||
# This is the place to define custom associations, delegations, scopes and | ||
# other ActiveRecord stuff | ||
def self.prepended(base) | ||
base.has_many :comments, dependent: :destroy | ||
base.scope :sellable, -> { base.where(...).order(...) } | ||
base.delegate :something, to: :something | ||
end | ||
|
||
... | ||
|
||
Spree::Product.prepend self | ||
end | ||
``` | ||
|
||
In this example, we're extending the functionality of `Spree::Product`. We | ||
include an ActiveRecord association, scope, and delegation. | ||
|
||
## Monkey patches and Solidus upgrades | ||
|
||
Monkey patches can complicate your Solidus upgrades. If you depend on them, | ||
[ensure](ensure) that you test them before upgrading in a production environment. Note | ||
that Solidus's core classes may change with each release. |