@spree/docs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/README.md +54 -0
  2. package/dist/api-reference/platform/authentication.md +38 -0
  3. package/dist/api-reference/store-api/authentication.md +188 -0
  4. package/dist/api-reference/store-api/errors.md +277 -0
  5. package/dist/api-reference/store-api/idempotency.md +129 -0
  6. package/dist/api-reference/store-api/introduction.md +34 -0
  7. package/dist/api-reference/store-api/localization.md +279 -0
  8. package/dist/api-reference/store-api/metadata.md +160 -0
  9. package/dist/api-reference/store-api/monetary-amounts.md +65 -0
  10. package/dist/api-reference/store-api/querying.md +399 -0
  11. package/dist/api-reference/store-api/rate-limitting.md +103 -0
  12. package/dist/api-reference/store-api/relations.md +185 -0
  13. package/dist/api-reference/storefront/authentication.md +88 -0
  14. package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
  15. package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
  16. package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
  17. package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
  18. package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
  19. package/dist/api-reference/v2/introduction.md +22 -0
  20. package/dist/api-reference/v2/pagination.md +37 -0
  21. package/dist/api-reference/webhooks-events.md +883 -0
  22. package/dist/developer/admin/admin.md +205 -0
  23. package/dist/developer/admin/authentication.md +59 -0
  24. package/dist/developer/admin/components.md +711 -0
  25. package/dist/developer/admin/custom-css.md +243 -0
  26. package/dist/developer/admin/custom-javascript.md +116 -0
  27. package/dist/developer/admin/extending-ui.md +1964 -0
  28. package/dist/developer/admin/form-builder.md +444 -0
  29. package/dist/developer/admin/helper-methods.md +531 -0
  30. package/dist/developer/admin/navigation.md +805 -0
  31. package/dist/developer/admin/tables.md +491 -0
  32. package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
  33. package/dist/developer/cli/quickstart.md +137 -0
  34. package/dist/developer/contributing/creating-an-extension.md +258 -0
  35. package/dist/developer/contributing/developing-spree.md +339 -0
  36. package/dist/developer/contributing/quickstart.md +32 -0
  37. package/dist/developer/contributing/updating-extensions.md +67 -0
  38. package/dist/developer/core-concepts/addresses.md +265 -0
  39. package/dist/developer/core-concepts/adjustments.md +107 -0
  40. package/dist/developer/core-concepts/architecture.md +177 -0
  41. package/dist/developer/core-concepts/calculators.md +323 -0
  42. package/dist/developer/core-concepts/customers.md +230 -0
  43. package/dist/developer/core-concepts/events.md +624 -0
  44. package/dist/developer/core-concepts/imports-exports.md +698 -0
  45. package/dist/developer/core-concepts/inventory.md +191 -0
  46. package/dist/developer/core-concepts/markets.md +250 -0
  47. package/dist/developer/core-concepts/media.md +167 -0
  48. package/dist/developer/core-concepts/metafields.md +187 -0
  49. package/dist/developer/core-concepts/orders.md +328 -0
  50. package/dist/developer/core-concepts/payments.md +710 -0
  51. package/dist/developer/core-concepts/pricing.md +163 -0
  52. package/dist/developer/core-concepts/products.md +360 -0
  53. package/dist/developer/core-concepts/promotions.md +322 -0
  54. package/dist/developer/core-concepts/reports.md +206 -0
  55. package/dist/developer/core-concepts/search-filtering.md +237 -0
  56. package/dist/developer/core-concepts/shipments.md +212 -0
  57. package/dist/developer/core-concepts/slugs.md +111 -0
  58. package/dist/developer/core-concepts/staff-roles.md +123 -0
  59. package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
  60. package/dist/developer/core-concepts/stores.md +117 -0
  61. package/dist/developer/core-concepts/taxes.md +135 -0
  62. package/dist/developer/core-concepts/translations.md +120 -0
  63. package/dist/developer/core-concepts/users.md +299 -0
  64. package/dist/developer/core-concepts/webhooks.md +378 -0
  65. package/dist/developer/create-spree-app/quickstart.md +158 -0
  66. package/dist/developer/customization/api.md +93 -0
  67. package/dist/developer/customization/authentication.md +88 -0
  68. package/dist/developer/customization/checkout.md +204 -0
  69. package/dist/developer/customization/configuration.md +55 -0
  70. package/dist/developer/customization/decorators.md +523 -0
  71. package/dist/developer/customization/dependencies.md +232 -0
  72. package/dist/developer/customization/emails.md +21 -0
  73. package/dist/developer/customization/extensions.md +92 -0
  74. package/dist/developer/customization/metadata.md +236 -0
  75. package/dist/developer/customization/model-preferences.md +130 -0
  76. package/dist/developer/customization/permissions.md +265 -0
  77. package/dist/developer/customization/quickstart.md +229 -0
  78. package/dist/developer/customization/routes.md +24 -0
  79. package/dist/developer/customization/v4/admin-panel.md +78 -0
  80. package/dist/developer/customization/v4/authentication.md +210 -0
  81. package/dist/developer/customization/v4/checkout.md +212 -0
  82. package/dist/developer/customization/v4/deface.md +251 -0
  83. package/dist/developer/customization/v4/images.md +86 -0
  84. package/dist/developer/customization/v4/storefront.md +450 -0
  85. package/dist/developer/deployment/assets.md +87 -0
  86. package/dist/developer/deployment/aws.md +335 -0
  87. package/dist/developer/deployment/caching.md +27 -0
  88. package/dist/developer/deployment/cdn.md +39 -0
  89. package/dist/developer/deployment/database.md +155 -0
  90. package/dist/developer/deployment/docker.md +128 -0
  91. package/dist/developer/deployment/emails.md +77 -0
  92. package/dist/developer/deployment/environment_variables.md +111 -0
  93. package/dist/developer/deployment/heroku.md +51 -0
  94. package/dist/developer/deployment/render.md +95 -0
  95. package/dist/developer/getting-started/quickstart.md +82 -0
  96. package/dist/developer/how-to/custom-payment-method.md +374 -0
  97. package/dist/developer/how-to/custom-promotion.md +373 -0
  98. package/dist/developer/how-to/custom-report.md +387 -0
  99. package/dist/developer/how-to/custom-search-provider.md +230 -0
  100. package/dist/developer/multi-store/quickstart.md +71 -0
  101. package/dist/developer/multi-store/setup.md +38 -0
  102. package/dist/developer/multi-tenant/configuration.md +41 -0
  103. package/dist/developer/multi-tenant/core-concepts.md +75 -0
  104. package/dist/developer/multi-tenant/installation.md +96 -0
  105. package/dist/developer/multi-tenant/quickstart.md +20 -0
  106. package/dist/developer/multi-vendor/installation.md +45 -0
  107. package/dist/developer/multi-vendor/quickstart.md +17 -0
  108. package/dist/developer/sdk/admin/quickstart.md +22 -0
  109. package/dist/developer/sdk/authentication.md +89 -0
  110. package/dist/developer/sdk/configuration.md +225 -0
  111. package/dist/developer/sdk/quickstart.md +82 -0
  112. package/dist/developer/sdk/store/account.md +67 -0
  113. package/dist/developer/sdk/store/cart-checkout.md +140 -0
  114. package/dist/developer/sdk/store/markets.md +151 -0
  115. package/dist/developer/sdk/store/payments.md +96 -0
  116. package/dist/developer/sdk/store/products.md +149 -0
  117. package/dist/developer/sdk/store/wishlists.md +52 -0
  118. package/dist/developer/security/pci_compliance.md +15 -0
  119. package/dist/developer/security/security_policy.md +68 -0
  120. package/dist/developer/storefront/blocks.md +285 -0
  121. package/dist/developer/storefront/custom-css.md +260 -0
  122. package/dist/developer/storefront/custom-javascript.md +166 -0
  123. package/dist/developer/storefront/helper-methods.md +1288 -0
  124. package/dist/developer/storefront/links.md +298 -0
  125. package/dist/developer/storefront/nextjs/architecture.md +150 -0
  126. package/dist/developer/storefront/nextjs/customization.md +141 -0
  127. package/dist/developer/storefront/nextjs/deployment.md +180 -0
  128. package/dist/developer/storefront/nextjs/quickstart.md +92 -0
  129. package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
  130. package/dist/developer/storefront/pages.md +163 -0
  131. package/dist/developer/storefront/sections.md +569 -0
  132. package/dist/developer/storefront/storefront.md +56 -0
  133. package/dist/developer/storefront/themes.md +161 -0
  134. package/dist/developer/tutorial/admin.md +134 -0
  135. package/dist/developer/tutorial/extending-models.md +380 -0
  136. package/dist/developer/tutorial/file-uploads.md +121 -0
  137. package/dist/developer/tutorial/introduction.md +33 -0
  138. package/dist/developer/tutorial/model.md +41 -0
  139. package/dist/developer/tutorial/page-builder.md +487 -0
  140. package/dist/developer/tutorial/rich-text.md +73 -0
  141. package/dist/developer/tutorial/seo.md +332 -0
  142. package/dist/developer/tutorial/storefront.md +352 -0
  143. package/dist/developer/tutorial/testing.md +558 -0
  144. package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
  145. package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
  146. package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
  147. package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
  148. package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
  149. package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
  150. package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
  151. package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
  152. package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
  153. package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
  154. package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
  155. package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
  156. package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
  157. package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
  158. package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
  159. package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
  160. package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
  161. package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
  162. package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
  163. package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
  164. package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
  165. package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
  166. package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
  167. package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
  168. package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
  169. package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
  170. package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
  171. package/dist/developer/upgrades/quickstart.md +36 -0
  172. package/dist/integrations/analytics/google-analytics.md +64 -0
  173. package/dist/integrations/analytics/google-tag-manager.md +78 -0
  174. package/dist/integrations/integrations.md +39 -0
  175. package/dist/integrations/marketing/klaviyo.md +99 -0
  176. package/dist/integrations/payments/adyen.md +90 -0
  177. package/dist/integrations/payments/paypal.md +41 -0
  178. package/dist/integrations/payments/razorpay.md +45 -0
  179. package/dist/integrations/payments/stripe.md +109 -0
  180. package/dist/integrations/search/meilisearch.md +236 -0
  181. package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
  182. package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
  183. package/package.json +27 -0
@@ -0,0 +1,523 @@
1
+ ---
2
+ title: Decorators
3
+ description: Decorators allow you to add or modify behavior of Spree classes in your application.
4
+ ---
5
+
6
+ > **WARNING:** **Decorators should be a last resort.** They tightly couple your code to Spree internals and can break during upgrades. Before using decorators, consider these modern alternatives that are safer and easier to maintain:
7
+ >
8
+ > - **[Events & Subscribers](/developer/core-concepts/events)** - For reacting to model changes (after save, create, update, delete)
9
+ > - **[Webhooks](/developer/core-concepts/webhooks)** - For notifying external services when events occur
10
+ > - **[Dependencies](/developer/customization/dependencies)** - For swapping out services, serializers, and abilities
11
+ > - **[Admin Navigation](/developer/admin/navigation)** - For adding menu items without controller decorators
12
+ > - **[Admin Partials](/developer/admin/extending-ui)** - For extending admin UI without view decorators
13
+ > - **[Admin Tables](/developer/admin/tables)** - For customizing admin list views
14
+ > - **[Ransack Configuration](/developer/core-concepts/search-filtering#extending-ransackable-configuration)** - For adding searchable/sortable fields without model decorators
15
+
16
+ ## When to Use Decorators vs Modern Alternatives
17
+
18
+ Before reaching for a decorator, check if your use case is better served by a modern alternative:
19
+
20
+ | Use Case | Instead of Decorator | Use This |
21
+ |----------|---------------------|----------|
22
+ | After-save hooks (sync to external service) | Model decorator with `after_save` | [Events subscriber](/developer/core-concepts/events) |
23
+ | Notify external service on changes | Model decorator with callbacks | [Webhooks](/developer/core-concepts/webhooks) |
24
+ | Custom add-to-cart logic | Service decorator | [Dependencies injection](/developer/customization/dependencies) |
25
+ | Custom API responses | Serializer decorator | [Dependencies injection](/developer/customization/dependencies) |
26
+ | Add admin menu item | Controller decorator | [Admin Navigation API](/developer/admin/navigation) |
27
+ | Add section to admin form | View decorator/override | [Admin Partials injection](/developer/admin/extending-ui) |
28
+ | Add searchable/filterable field | Model decorator with `ransackable_attributes` | [Ransack configuration](/developer/core-concepts/search-filtering#extending-ransackable-configuration) |
29
+ | Add association to core model | - | Decorator (still appropriate) |
30
+ | Add validation to core model | - | Decorator (still appropriate) |
31
+ | Add new method to core model | - | Decorator (still appropriate) |
32
+
33
+ > **INFO:** Decorators are still appropriate for **structural changes** like adding associations, validations, scopes, and new methods to models. Use modern alternatives for **behavioral changes** like callbacks, hooks, and side effects.
34
+
35
+ ## Overview
36
+
37
+ All of Spree's models, controllers, helpers, etc can easily be extended or overridden to meet your exact requirements using standard Ruby idioms.
38
+
39
+ Standard practice for including such changes in your application or extension is to create a file within the relevant **app/models/spree** or **app/controllers/spree** directory with the original class name with **_decorator** appended.
40
+
41
+ ## Why Use Decorators?
42
+
43
+ When working with Spree, you'll often need to add functionality to existing models like `Spree::Product` or `Spree::Order`. However, you shouldn't modify these files directly because:
44
+
45
+ 1. **Upgrades** - Your changes would be lost when updating Spree
46
+ 2. **Maintainability** - It's hard to track what you've customized
47
+ 3. **Conflicts** - Direct modifications can conflict with Spree's code
48
+
49
+ Instead, we use **decorators** - a Ruby pattern that lets you add or modify behavior of existing classes without changing their original source code.
50
+
51
+ ## How Decorators Work
52
+
53
+ In Ruby, classes are "open" - you can add methods to them at any time. Decorators leverage this by:
54
+
55
+ 1. Creating a module with your new methods
56
+ 2. Using `Module#prepend` to inject your module into the class's inheritance chain
57
+ 3. Your methods run first, and can call `super` to invoke the original method
58
+
59
+ ```ruby
60
+ # This is the basic pattern
61
+ module Spree
62
+ module ProductDecorator
63
+ # Add a new method
64
+ def my_new_method
65
+ "Hello from decorator!"
66
+ end
67
+
68
+ # Override an existing method
69
+ def existing_method
70
+ # Do something before
71
+ result = super # Call the original method
72
+ # Do something after
73
+ result
74
+ end
75
+ end
76
+
77
+ Product.prepend(ProductDecorator)
78
+ end
79
+ ```
80
+
81
+ The key line is `Product.prepend(ProductDecorator)` - this inserts your module at the beginning of the method lookup chain, so your methods are found first.
82
+
83
+ ## Generating Decorators
84
+
85
+ Spree provides generators to create decorator files with the correct structure:
86
+
87
+ ### Model Decorator Generator
88
+
89
+ ```bash
90
+ bin/rails g spree:model_decorator Spree::Product
91
+ ```
92
+
93
+ This creates `app/models/spree/product_decorator.rb`:
94
+
95
+ ```ruby
96
+ module Spree
97
+ module ProductDecorator
98
+ def self.prepended(base)
99
+ # Class-level configurations go here
100
+ end
101
+ end
102
+
103
+ Product.prepend(ProductDecorator)
104
+ end
105
+ ```
106
+
107
+ ### Controller Decorator Generator
108
+
109
+ ```bash
110
+ bin/rails g spree:controller_decorator Spree::Admin::ProductsController
111
+ ```
112
+
113
+ This creates `app/controllers/spree/admin/products_controller_decorator.rb`:
114
+
115
+ ```ruby
116
+ module Spree
117
+ module Admin
118
+ module ProductsControllerDecorator
119
+ def self.prepended(base)
120
+ # Class-level configurations go here
121
+ end
122
+ end
123
+
124
+ ProductsController.prepend(ProductsControllerDecorator)
125
+ end
126
+ end
127
+ ```
128
+
129
+ ## Decorating Models
130
+
131
+ ### Changing Behavior of Existing Methods
132
+
133
+ The most common use case is changing the behavior of existing methods. When overriding a method, you can call `super` to invoke the original implementation:
134
+
135
+ ```ruby app/models/spree/product_decorator.rb
136
+ module Spree
137
+ module ProductDecorator
138
+ def available?
139
+ # Add custom logic before
140
+ return false if discontinued?
141
+
142
+ # Call the original method
143
+ super
144
+ end
145
+ end
146
+
147
+ Product.prepend(ProductDecorator)
148
+ end
149
+ ```
150
+
151
+ > **WARNING:** Always consider whether you need to call `super` when overriding methods. Omitting it completely replaces the original behavior, which may break functionality.
152
+
153
+ ### Adding New Methods
154
+
155
+ Add new instance methods directly in the decorator module:
156
+
157
+ ```ruby app/models/spree/product_decorator.rb
158
+ module Spree
159
+ module ProductDecorator
160
+ def featured?
161
+ metadata[:featured] == true
162
+ end
163
+
164
+ def days_until_available
165
+ return 0 if available_on.nil? || available_on <= Time.current
166
+ (available_on.to_date - Date.current).to_i
167
+ end
168
+ end
169
+
170
+ Product.prepend(ProductDecorator)
171
+ end
172
+ ```
173
+
174
+ ### Adding Associations
175
+
176
+ Use the `self.prepended(base)` callback to add associations:
177
+
178
+ ```ruby app/models/spree/product_decorator.rb
179
+ module Spree
180
+ module ProductDecorator
181
+ def self.prepended(base)
182
+ base.belongs_to :brand, class_name: 'Spree::Brand', optional: true
183
+ base.has_many :videos, class_name: 'Spree::Video', dependent: :destroy
184
+ end
185
+ end
186
+
187
+ Product.prepend(ProductDecorator)
188
+ end
189
+ ```
190
+
191
+ ### Adding Validations
192
+
193
+ ```ruby app/models/spree/product_decorator.rb
194
+ module Spree
195
+ module ProductDecorator
196
+ def self.prepended(base)
197
+ base.validates :external_id, presence: true, uniqueness: true
198
+ base.validates :weight, numericality: { greater_than: 0 }, allow_nil: true
199
+ end
200
+ end
201
+
202
+ Product.prepend(ProductDecorator)
203
+ end
204
+ ```
205
+
206
+ ### Adding Scopes
207
+
208
+ ```ruby app/models/spree/product_decorator.rb
209
+ module Spree
210
+ module ProductDecorator
211
+ def self.prepended(base)
212
+ base.scope :featured, -> { where("metadata->>'featured' = ?", 'true') }
213
+ base.scope :recently_added, -> { where('created_at > ?', 30.days.ago) }
214
+ base.scope :on_sale, -> { joins(:variants).where('spree_prices.compare_at_amount > spree_prices.amount') }
215
+ end
216
+ end
217
+
218
+ Product.prepend(ProductDecorator)
219
+ end
220
+ ```
221
+
222
+ ### Adding Class Methods
223
+
224
+ Use `extend` within the `prepended` callback to add class methods:
225
+
226
+ ```ruby app/models/spree/product_decorator.rb
227
+ module Spree
228
+ module ProductDecorator
229
+ def self.prepended(base)
230
+ base.extend ClassMethods
231
+ end
232
+
233
+ module ClassMethods
234
+ def search_by_name(query)
235
+ where('LOWER(name) LIKE ?', "%#{query.downcase}%")
236
+ end
237
+ end
238
+ end
239
+
240
+ Product.prepend(ProductDecorator)
241
+ end
242
+ ```
243
+
244
+ Usage:
245
+
246
+ ```ruby
247
+ Spree::Product.search_by_name('shirt')
248
+ ```
249
+
250
+ ## Decorating Controllers
251
+
252
+ > **WARNING:** **Consider creating a new controller instead of decorating.** Creating your own controller that inherits from Spree's base controllers is more maintainable and less likely to break during upgrades. Use controller decorators only when you must modify existing Spree actions.
253
+
254
+ ### Recommended: Create a New Controller
255
+
256
+ Instead of decorating `Spree::ProductsController` to add a new action, create your own controller:
257
+
258
+ ```ruby app/controllers/spree/product_quick_views_controller.rb
259
+ module Spree
260
+ class ProductQuickViewsController < StoreController
261
+ def show
262
+ @product = current_store.products.friendly.find(params[:product_id])
263
+ render partial: 'spree/products/quick_view', locals: { product: @product }
264
+ end
265
+ end
266
+ end
267
+ ```
268
+
269
+ ```ruby config/routes.rb
270
+ Spree::Core::Engine.add_routes do
271
+ get 'products/:product_id/quick_view', to: 'product_quick_views#show', as: :product_quick_view
272
+ end
273
+ ```
274
+
275
+ This approach:
276
+ - Won't break when Spree updates `ProductsController`
277
+ - Is easier to test in isolation
278
+ - Makes your customizations clearly visible in your codebase
279
+
280
+ ### Adding a New Action via Decorator
281
+
282
+ If you must add an action to an existing controller:
283
+
284
+ ```ruby app/controllers/spree/products_controller_decorator.rb
285
+ module Spree
286
+ module ProductsControllerDecorator
287
+ def self.prepended(base)
288
+ base.before_action :load_product, only: [:quick_view]
289
+ end
290
+
291
+ def quick_view
292
+ respond_to do |format|
293
+ format.html { render partial: 'quick_view', locals: { product: @product } }
294
+ format.json { render json: @product }
295
+ end
296
+ end
297
+
298
+ private
299
+
300
+ def load_product
301
+ @product = current_store.products.friendly.find(params[:id])
302
+ end
303
+ end
304
+
305
+ ProductsController.prepend(ProductsControllerDecorator)
306
+ end
307
+ ```
308
+
309
+ Don't forget to add the route:
310
+
311
+ ```ruby config/routes.rb
312
+ Spree::Core::Engine.add_routes do
313
+ get 'products/:id/quick_view', to: 'products#quick_view', as: :product_quick_view
314
+ end
315
+ ```
316
+
317
+ ### Modifying Existing Actions
318
+
319
+ > **WARNING:** **High risk of breaking during upgrades.** When you override an existing Spree action, your code depends on Spree's internal implementation details. If Spree changes the action's behavior, instance variables, or method signatures in a future version, your decorator may silently break or cause unexpected bugs. Use [Events](/developer/core-concepts/events) for post-action side effects instead.
320
+
321
+ ```ruby app/controllers/spree/admin/products_controller_decorator.rb
322
+ module Spree
323
+ module Admin
324
+ module ProductsControllerDecorator
325
+ def create
326
+ # Add custom logic before
327
+ log_product_creation_attempt
328
+
329
+ # Call original method
330
+ super
331
+
332
+ # Add custom logic after
333
+ notify_team_of_new_product if @product.persisted?
334
+ end
335
+
336
+ private
337
+
338
+ def log_product_creation_attempt
339
+ Rails.logger.info "Product creation attempted by #{current_spree_user.email}"
340
+ end
341
+
342
+ def notify_team_of_new_product
343
+ ProductNotificationJob.perform_later(@product)
344
+ end
345
+ end
346
+
347
+ ProductsController.prepend(ProductsControllerDecorator)
348
+ end
349
+ end
350
+ ```
351
+
352
+ > **INFO:** **Better alternative:** For post-action side effects like notifications, use [Events subscribers](/developer/core-concepts/events) instead. Subscribe to `product.created` to be notified when products are created, without coupling to controller internals.
353
+
354
+ ### Adding Before Actions
355
+
356
+ ```ruby app/controllers/spree/checkout_controller_decorator.rb
357
+ module Spree
358
+ module CheckoutControllerDecorator
359
+ def self.prepended(base)
360
+ base.before_action :check_minimum_order, only: [:update]
361
+ end
362
+
363
+ private
364
+
365
+ def check_minimum_order
366
+ if @order.total < 25.0 && params[:state] == 'payment'
367
+ flash[:error] = 'Minimum order amount is $25'
368
+ redirect_to checkout_state_path(@order.state)
369
+ end
370
+ end
371
+ end
372
+
373
+ CheckoutController.prepend(CheckoutControllerDecorator)
374
+ end
375
+ ```
376
+
377
+ ## Best Practices
378
+
379
+
380
+ - **Use the prepended callback** — Always use `self.prepended(base)` for class-level additions like associations, validations, scopes, and callbacks.
381
+
382
+ - **Keep decorators focused** — Each decorator should have a single responsibility. Create multiple decorators for different concerns if needed.
383
+
384
+ - **Call super when overriding** — When overriding methods, call `super` to preserve original behavior unless you intentionally want to replace it entirely.
385
+
386
+ - **Test decorated behavior** — Write tests specifically for your decorated functionality to catch regressions during upgrades.
387
+
388
+
389
+ ### Organizing Multiple Decorators
390
+
391
+ If you have many customizations for a single class, consider splitting them into focused decorators:
392
+
393
+ ```
394
+ app/models/spree/
395
+ ├── product_decorator.rb # Main decorator (loads others)
396
+ ├── product/
397
+ │ ├── brand_decorator.rb # Brand association
398
+ │ ├── inventory_decorator.rb # Inventory customizations
399
+ │ └── seo_decorator.rb # SEO-related methods
400
+ ```
401
+
402
+ ```ruby app/models/spree/product_decorator.rb
403
+ # Load focused decorators
404
+ require_dependency 'spree/product/brand_decorator'
405
+ require_dependency 'spree/product/inventory_decorator'
406
+ require_dependency 'spree/product/seo_decorator'
407
+ ```
408
+
409
+ ## Common Pitfalls
410
+
411
+ ### Forgetting to Call Super
412
+
413
+ ```ruby
414
+ # ❌ Bad - completely replaces original behavior
415
+ def available?
416
+ in_stock? && active?
417
+ end
418
+
419
+ # ✅ Good - extends original behavior
420
+ def available?
421
+ super && custom_availability_check
422
+ end
423
+ ```
424
+
425
+ ### Using Instance Variables in prepended
426
+
427
+ ```ruby
428
+ # ❌ Bad - instance variables don't work in prepended
429
+ def self.prepended(base)
430
+ @custom_setting = true # This won't work as expected
431
+ end
432
+
433
+ # ✅ Good - use class attributes or methods
434
+ def self.prepended(base)
435
+ base.class_attribute :custom_setting, default: true
436
+ end
437
+ ```
438
+
439
+ ### Circular Dependencies
440
+
441
+ Be careful when decorators depend on each other:
442
+
443
+ ```ruby
444
+ # ❌ Bad - can cause loading issues
445
+ # product_decorator.rb
446
+ def self.prepended(base)
447
+ base.has_many :variants # Variant decorator might not be loaded yet
448
+ end
449
+
450
+ # ✅ Good - use strings for class names
451
+ def self.prepended(base)
452
+ base.has_many :variants, class_name: 'Spree::Variant'
453
+ end
454
+ ```
455
+
456
+ ## Migrating from Decorators to Modern Patterns
457
+
458
+ If you have existing decorators that use callbacks for side effects, consider migrating them to Events subscribers for better maintainability.
459
+
460
+ ### Example: Migrating an After-Save Callback
461
+
462
+ **Before (Decorator with callback):**
463
+
464
+ ```ruby app/models/spree/product_decorator.rb
465
+ module Spree
466
+ module ProductDecorator
467
+ def self.prepended(base)
468
+ base.after_save :sync_to_external_service
469
+ end
470
+
471
+ private
472
+
473
+ def sync_to_external_service
474
+ ExternalSyncJob.perform_later(self) if saved_change_to_name?
475
+ end
476
+ end
477
+
478
+ Product.prepend(ProductDecorator)
479
+ end
480
+ ```
481
+
482
+ **After (Events subscriber):**
483
+
484
+ ```ruby app/subscribers/my_app/product_sync_subscriber.rb
485
+ module MyApp
486
+ class ProductSyncSubscriber < Spree::Subscriber
487
+ subscribes_to 'product.updated'
488
+
489
+ def handle(event)
490
+ product = Spree::Product.find_by(id: event.payload['id'])
491
+ return unless product
492
+
493
+ # The payload includes changes, check if name changed
494
+ if event.payload['previous_changes']&.key?('name')
495
+ ExternalSyncJob.perform_later(product)
496
+ end
497
+ end
498
+ end
499
+ end
500
+ ```
501
+
502
+ ### Benefits of Migration
503
+
504
+
505
+ - **Loose coupling** — Your code doesn't depend on Spree internals. Events provide a stable interface.
506
+
507
+ - **Easier upgrades** — Events-based code is less likely to break when Spree is updated.
508
+
509
+ - **Better testability** — Subscribers can be tested in isolation without loading the full model.
510
+
511
+ - **Async by default** — Subscribers run via ActiveJob, keeping your requests fast.
512
+
513
+
514
+ ## Related Documentation
515
+
516
+ - [Events](/developer/core-concepts/events) - Learn about Spree's event system
517
+ - [Webhooks](/developer/core-concepts/webhooks) - HTTP callbacks for external integrations
518
+ - [Dependencies](/developer/customization/dependencies) - Swap core services with your own
519
+ - [Admin Navigation](/developer/admin/navigation) - Extend admin menu without decorators
520
+ - [Admin Partials](/developer/admin/extending-ui) - Extend admin UI without view decorators
521
+ - [Extending Core Models Tutorial](/developer/tutorial/extending-models) - Step-by-step guide to connecting custom models with Spree core
522
+ - [Customization Overview](/developer/customization/quickstart) - General customization patterns
523
+ - [Logic Customization](/developer/customization/logic) - Customizing business logic