@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,137 @@
1
+ ---
2
+ title: Spree CLI
3
+ sidebarTitle: Overview
4
+ description: Manage your Spree Commerce project from the command line.
5
+ ---
6
+
7
+ The Spree CLI (`@spree/cli`) manages Docker-based Spree projects created with [create-spree-app](/developer/create-spree-app/quickstart).
8
+
9
+ ## Installation
10
+
11
+ The CLI is included automatically when you scaffold a project with `create-spree-app`. You can also install it globally:
12
+
13
+ ```bash
14
+ npm install -g @spree/cli
15
+ ```
16
+
17
+ Then run commands from your project directory:
18
+
19
+ ```bash
20
+ spree dev
21
+ ```
22
+
23
+ Or use `npx` without installing:
24
+
25
+ ```bash
26
+ npx @spree/cli dev
27
+ ```
28
+
29
+ ## Commands
30
+
31
+ ### `spree init`
32
+
33
+ First-run setup. Starts Docker services, seeds the database, generates an API key, and optionally loads sample data.
34
+
35
+ ```bash
36
+ spree init
37
+ spree init --no-sample-data # Skip sample data
38
+ spree init --no-open # Skip opening browser
39
+ ```
40
+
41
+ ### `spree dev`
42
+
43
+ Start services and stream logs.
44
+
45
+ ```bash
46
+ spree dev
47
+ ```
48
+
49
+ ### `spree stop`
50
+
51
+ Stop all services.
52
+
53
+ ```bash
54
+ spree stop
55
+ ```
56
+
57
+ ### `spree update`
58
+
59
+ Pull the latest Spree Docker image and recreate containers. Migrations run automatically on startup.
60
+
61
+ ```bash
62
+ spree update
63
+ ```
64
+
65
+ ### `spree eject`
66
+
67
+ Switch from the prebuilt Docker image to building from your local `backend/` directory. This lets you customize the Rails app — add gems, override models, add migrations, etc.
68
+
69
+ ```bash
70
+ spree eject
71
+ ```
72
+
73
+ After ejecting, the Docker image is built from `backend/Dockerfile`. Edit files in `backend/` and run `spree dev` to rebuild and restart with your changes.
74
+
75
+ See [Customizing the Backend](/developer/create-spree-app/quickstart#customizing-the-backend) for details on what you can customize.
76
+
77
+ ### `spree logs`
78
+
79
+ Stream service logs.
80
+
81
+ ```bash
82
+ spree logs # Web service (default)
83
+ spree logs worker # Worker service
84
+ ```
85
+
86
+ ### `spree console`
87
+
88
+ Open a Rails console.
89
+
90
+ ```bash
91
+ spree console
92
+ ```
93
+
94
+ ### `spree open`
95
+
96
+ Open the admin dashboard in the browser.
97
+
98
+ ```bash
99
+ spree open
100
+ ```
101
+
102
+ ### `spree seed`
103
+
104
+ Seed the database.
105
+
106
+ ```bash
107
+ spree seed
108
+ ```
109
+
110
+ ### `spree sample-data`
111
+
112
+ Load sample products, categories, and images.
113
+
114
+ ```bash
115
+ spree sample-data
116
+ ```
117
+
118
+ ### `spree user create`
119
+
120
+ Create an admin user. Prompts for email and password interactively, or pass them as flags:
121
+
122
+ ```bash
123
+ spree user create
124
+ spree user create --email admin@example.com --password secret123
125
+ ```
126
+
127
+ ### `spree api-key`
128
+
129
+ Manage Store and Admin API keys.
130
+
131
+ ```bash
132
+ spree api-key list # List all keys
133
+ spree api-key create # Interactive
134
+ spree api-key create --name "Storefront" --type publishable # Store API key
135
+ spree api-key create --name "Admin" --type secret # Admin API key
136
+ spree api-key revoke <id> # Revoke a key
137
+ ```
@@ -0,0 +1,258 @@
1
+ ---
2
+ title: Creating an Extension
3
+ description: Learn how to create a Spree extension.
4
+ ---
5
+
6
+ ## Overview
7
+
8
+ [Spree Extensions](/developer/customization/extensions) are a way to add new functionality to your Spree store. They are a great way to extend the functionality of Spree and add new features. You can share them with Spree community on Github so anyone can use them, contribute back and share your improvements.
9
+
10
+ > **INFO:** This tutorial uses decorators for extending Spree models. For extensions that need to react to events (sync with external services, send notifications, etc.), consider using [Events subscribers](/developer/core-concepts/events) instead - they're easier to test and maintain. See [Customization Quickstart](/developer/customization/quickstart) for guidance on choosing the right approach.
11
+
12
+ ## Getting Started
13
+
14
+ Let's build a simple extension. Suppose we want the ability to mark certain products as being on sale. We'd like to be able to set a sale price on a product and show products that are on sale on a separate products page. This is a great example of how an extension can be used to build on the solid Spree foundation.
15
+
16
+ Run the following command from a directory of your choice outside of our Spree application:
17
+
18
+ ```bash
19
+ spree extension simple_sales
20
+ ```
21
+
22
+ This creates a `spree_simple_sales` directory with several additional files and directories. After generating the extension make sure you change to its directory:
23
+
24
+ ```bash
25
+ cd spree_simple_sales
26
+ ```
27
+
28
+ ## Adding a Sale Price to Variants
29
+
30
+ The first thing we need to do is create a migration that adds a sale_price column to [variants](/developer/core-concepts/products#variants).
31
+
32
+ We can do this with the following command:
33
+
34
+ ```bash
35
+ bin/rails g migration add_sale_price_to_spree_variants sale_price:decimal
36
+ ```
37
+
38
+ Because we are dealing with prices, we need to now edit the generated migration to ensure the correct precision and scale. Edit the file `db/migrate/XXXXXXXXXXX_add_sale_price_to_spree_variants.rb` so that it contains the following:
39
+
40
+ ```ruby
41
+ class AddSalePriceToSpreeVariants < ActiveRecord::Migration[7.1]
42
+ def change
43
+ add_column :spree_variants, :sale_price, :decimal, precision: 8, scale: 2
44
+ end
45
+ end
46
+ ```
47
+
48
+ ## Adding Our Extension to the Spree Application
49
+
50
+ Before we continue development of our extension, let's add it to the Spree application.
51
+
52
+ Within the `my_store` application directory, add the following line to the bottom of our `Gemfile`:
53
+
54
+ ```ruby
55
+ gem 'spree_simple_sales', path: '../spree_simple_sales'
56
+ ```
57
+
58
+ You may have to adjust the path somewhat depending on where you created the extension. You want this to be the path relative to the location of the `my_store` application.
59
+
60
+ Once you have added the gem, it's time to bundle:
61
+
62
+ ```bash
63
+ bundle install
64
+ ```
65
+
66
+ Finally, let's run the `spree_simple_sales` install generator to copy over the migration we just created answer **yes** if prompted to run migrations:
67
+
68
+ ```bash
69
+ # context: Your Spree store's app root (i.e. Rails.root); not the extension's root path.
70
+ bin/rails g spree_simple_sales:install
71
+ ```
72
+
73
+ ## Adding a Controller Action to HomeController
74
+
75
+ Now we need to extend `Spree::HomeController` and add an action that selects "on sale" products.
76
+
77
+ Note for the sake of this example that \`Spree::HomeController\` is only included in spree_storefront so you need to make it a dependency on your extensions \*.gemspec file.
78
+
79
+ Make sure you are in the `spree_simple_sales` root directory and run the following command to create the directory structure for our controller decorator:
80
+
81
+ ```bash
82
+ mkdir -p app/controllers/spree_simple_sales
83
+ ```
84
+
85
+ Next, create a new file in the directory we just created called `home_controller_decorator.rb` and add the following content to it:
86
+
87
+ ```ruby
88
+ module SpreeSimpleSales
89
+ module HomeControllerDecorator
90
+ def sale
91
+ @products = Spree::Product.joins(:variants_including_master).where.not(sale_price: nil).distinct
92
+ end
93
+ end
94
+ end
95
+
96
+ Spree::HomeController.prepend SpreeSimpleSales::HomeControllerDecorator
97
+ ```
98
+
99
+ This will select just the products that have a variant with a `sale_price` set.
100
+
101
+ We also need to add a route to this action in our `config/routes.rb` file. Let's do this now. Update the routes file to contain the following:
102
+
103
+ ```ruby
104
+ Spree::Core::Engine.routes.draw do
105
+ get "/sale" => "home#sale"
106
+ end
107
+ ```
108
+
109
+ ## Viewing On Sale Products
110
+
111
+ ### Setting the Sale Price for a Variant
112
+
113
+ Now that our variants have the attribute `sale_price` available to them, let's update the sample data so we have at least one product that is on sale in our application. We will need to do this in the rails console for the time being, as we have no admin interface to set sale prices for variants. So, in order to do this, first open up the rails console:
114
+
115
+ ```bash
116
+ bin/rails c
117
+ ```
118
+
119
+ Now, follow the steps I take in selecting a product and updating its master variant to have a sale price. Note, you may not be editing the exact same product as I am, but this is not important. We just need one "on sale" product to display on the sales page.
120
+
121
+ ```ruby
122
+ > product = Spree::Product.first
123
+ => #<Spree::Product id: 107377505, name: "Spree Bag", description: "Lorem ipsum dolor sit amet, consectetuer adipiscing...", available_on: "2013-02-13 18:30:16", deleted_at: nil, permalink: "spree-bag", meta_description: nil, meta_keywords: nil, tax_category_id: 25484906, shipping_category_id: nil, count_on_hand: 10, created_at: "2013-02-13 18:30:16", updated_at: "2013-02-13 18:30:16", on_demand: false>
124
+
125
+ > variant = product.master
126
+ => #<Spree::Variant id: 833839126, sku: "SPR-00012", weight: nil, height: nil, width: nil, depth: nil, deleted_at: nil, is_master: true, product_id: 107377505, count_on_hand: 10, cost_price: #<BigDecimal:7f8dda5eebf0,'0.21E2',9(36)>, position: nil, lock_version: 0, on_demand: false, cost_currency: nil, sale_price: nil>
127
+
128
+ > variant.sale_price = 8.00
129
+ => 8.0
130
+
131
+ > variant.save
132
+ => true
133
+ ```
134
+
135
+ ## Decorating Variants
136
+
137
+ Let's fix our extension so that it uses the `sale_price` when it is present.
138
+
139
+ Next, create the file `app/models/spree_simple_sales/variant_decorator.rb` and add the following content to it:
140
+
141
+ ```ruby
142
+ module SpreeSimpleSales
143
+ module VariantDecorator
144
+ def price_in(currency)
145
+ return super unless sale_price.present?
146
+ Spree::Price.new(variant_id: self.id, amount: self.sale_price, currency: currency)
147
+ end
148
+ end
149
+ end
150
+
151
+ Spree::Variant.prepend SpreeSimpleSales::VariantDecorator
152
+ ```
153
+
154
+ If there is a `sale_price` present on the product's master variant, we return that price. Otherwise, we call the original implementation of `price_in` using `return super`.
155
+
156
+ ## Testing Our Decorator
157
+
158
+ It's always a good idea to test your code. We should be extra careful to write tests for our Variant decorator since we are modifying core Spree functionality. Let's write a couple of simple unit tests for `variant_decorator.rb`
159
+
160
+ ### Generating the Test App
161
+
162
+ An extension is not a full Rails application, so we need something to test our extension against. By running the Spree `test_app` rake task, we can generate a barebones Spree application within our `spec` directory to run our tests against.
163
+
164
+ We can do this with the following command from the root directory of our extension:
165
+
166
+ ```bash
167
+ bundle exec rake test_app
168
+ ```
169
+
170
+ After this command completes, you should be able to run `rspec` and see the following output:
171
+
172
+ ```bash
173
+ No examples found.
174
+
175
+ Finished in 0.00005 seconds
176
+ 0 examples, 0 failures
177
+ ```
178
+
179
+ Great! We're ready to start adding some tests. Let's replicate the extension's directory structure in our spec directory by running the following command
180
+
181
+ ```bash
182
+ mkdir -p spec/models/spree
183
+ ```
184
+
185
+ Now, let's create a new file in this directory called `variant_decorator_spec.rb` and add the following tests to it:
186
+
187
+ ```ruby
188
+ require 'spec_helper'
189
+
190
+ describe Spree::Variant do
191
+ describe "#price_in" do
192
+ it "returns the sale price if it is present" do
193
+ variant = create(:variant, sale_price: 8.00)
194
+ expected = Spree::Price.new(variant_id: variant.id, currency: "USD", amount: variant.sale_price)
195
+
196
+ result = variant.price_in("USD")
197
+
198
+ expect(result.variant_id).to eq(expected.variant_id)
199
+ expect(result.amount.to_f).to eq(expected.amount.to_f)
200
+ expect(result.currency).to eq(expected.currency)
201
+ end
202
+
203
+ it "returns the normal price if it is not on sale" do
204
+ variant = create(:variant, price: 15.00)
205
+ expected = Spree::Price.new(variant_id: variant.id, currency: "USD", amount: variant.price)
206
+
207
+ result = variant.price_in("USD")
208
+
209
+ expect(result.variant_id).to eq(expected.variant_id)
210
+ expect(result.amount.to_f).to eq(expected.amount.to_f)
211
+ expect(result.currency).to eq(expected.currency)
212
+ end
213
+ end
214
+ end
215
+ ```
216
+
217
+ These specs test that the `price_in` method we overrode in our `VariantDecorator` returns the correct price both when the sale price is present and when it is not.
218
+
219
+ ## Summary
220
+
221
+ In this tutorial, you learned how to both install extensions and create your own. A lot of core Spree development concepts were covered and you gained exposure to some of the Spree internals.
222
+
223
+ ## Alternative Approaches
224
+
225
+ While this tutorial uses decorators to extend Spree's core behavior, modern Spree provides additional patterns that may be more appropriate depending on your use case:
226
+
227
+ | Use Case | Recommended Approach |
228
+ |----------|---------------------|
229
+ | Structural changes (associations, validations) | Decorators (as shown in this tutorial) |
230
+ | React to model changes | [Events subscribers](/developer/core-concepts/events) |
231
+ | External service integration | [Webhooks](/developer/core-concepts/webhooks) |
232
+ | Replace core services | [Dependencies injection](/developer/customization/dependencies) |
233
+ | Add admin UI elements | [Admin Partials](/developer/admin/extending-ui) |
234
+ | Add admin menu items | [Admin Navigation](/developer/admin/navigation) |
235
+
236
+ For example, if your extension needs to sync data with an external service when products are updated, use an Events subscriber instead of a decorator callback:
237
+
238
+ ```ruby app/subscribers/my_extension/product_sync_subscriber.rb
239
+ module MyExtension
240
+ class ProductSyncSubscriber < Spree::Subscriber
241
+ subscribes_to 'product.updated'
242
+
243
+ def handle(event)
244
+ product = Spree::Product.find_by(id: event.payload['id'])
245
+ return unless product
246
+
247
+ ExternalService.sync(product)
248
+ end
249
+ end
250
+ end
251
+ ```
252
+
253
+ ## Related Documentation
254
+
255
+ - [Events](/developer/core-concepts/events) - Subscribe to Spree events
256
+ - [Webhooks](/developer/core-concepts/webhooks) - HTTP callbacks for external integrations
257
+ - [Dependencies](/developer/customization/dependencies) - Replace core services
258
+ - [Customization Quickstart](/developer/customization/quickstart) - Choose the right approach