workarea 3.4.12

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 (571) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +326 -0
  3. data/CHANGELOG.md +20501 -0
  4. data/README.md +163 -0
  5. data/docker-compose.yml +27 -0
  6. data/docs/Gemfile +8 -0
  7. data/docs/bin/middleman +29 -0
  8. data/docs/config.rb +87 -0
  9. data/docs/config.ru +7 -0
  10. data/docs/data/articles.yml +157 -0
  11. data/docs/package.json +15 -0
  12. data/docs/source/404.html.erb +13 -0
  13. data/docs/source/articles/access-routes-in-javascript.html.md +33 -0
  14. data/docs/source/articles/add-a-content-area.html.md +169 -0
  15. data/docs/source/articles/add-a-content-block-type.html.md +334 -0
  16. data/docs/source/articles/add-a-report.html.md +202 -0
  17. data/docs/source/articles/add-css-through-the-admin-ui.html.md +30 -0
  18. data/docs/source/articles/add-javascript-through-a-manifest.html.md +367 -0
  19. data/docs/source/articles/add-javascript-through-a-view.html.md +80 -0
  20. data/docs/source/articles/add-javascript-through-the-admin-ui.html.md +30 -0
  21. data/docs/source/articles/add-metrics.html.md +58 -0
  22. data/docs/source/articles/add-or-replace-a-pricing-calculator.html.md +150 -0
  23. data/docs/source/articles/add-remove-or-change-a-mongoid-validation.html.md +147 -0
  24. data/docs/source/articles/add-remove-or-change-a-product-template.html.md +142 -0
  25. data/docs/source/articles/add-remove-sort-and-group-storefront-search-filters.html.md +483 -0
  26. data/docs/source/articles/add-stylesheets-through-a-manifest.html.md +276 -0
  27. data/docs/source/articles/add-system-content.html.md +138 -0
  28. data/docs/source/articles/analytics-overview.html.md +51 -0
  29. data/docs/source/articles/analyze-storefront-search-results.html.md +261 -0
  30. data/docs/source/articles/api-overview.html.md +35 -0
  31. data/docs/source/articles/appending.html.md +506 -0
  32. data/docs/source/articles/application-document.html.md +88 -0
  33. data/docs/source/articles/automated-javascript-testing.html.md +162 -0
  34. data/docs/source/articles/b2b-overview.html.md +64 -0
  35. data/docs/source/articles/browser-and-device-support.html.md +47 -0
  36. data/docs/source/articles/change-product-placeholder-image.html.md +39 -0
  37. data/docs/source/articles/change-storefront-search-results.html.md +283 -0
  38. data/docs/source/articles/change-the-storefront-product-pricing-ui.html.md +348 -0
  39. data/docs/source/articles/change-the-storefront-search-filters-ui.html.md +103 -0
  40. data/docs/source/articles/checkout.html.md +479 -0
  41. data/docs/source/articles/commerce-model.html.md +164 -0
  42. data/docs/source/articles/configuration-for-hosting.html.md +106 -0
  43. data/docs/source/articles/configuration.html.md +406 -0
  44. data/docs/source/articles/configure-a-payment-gateway.html.md +58 -0
  45. data/docs/source/articles/configure-asset-storage.html.md +29 -0
  46. data/docs/source/articles/configure-asset-types.html.md +18 -0
  47. data/docs/source/articles/configure-contact-form-subjects-list.html.md +24 -0
  48. data/docs/source/articles/configure-imageoptim.html.md +23 -0
  49. data/docs/source/articles/configure-locales.html.md +45 -0
  50. data/docs/source/articles/configure-logins-and-authentication.html.md +42 -0
  51. data/docs/source/articles/configure-low-inventory-threshold.html.md +26 -0
  52. data/docs/source/articles/configure-product-image-sizes-and-processing.html.md +28 -0
  53. data/docs/source/articles/content.html.md +554 -0
  54. data/docs/source/articles/contentable.html.md +41 -0
  55. data/docs/source/articles/contribute-code.html.md +69 -0
  56. data/docs/source/articles/contribute-documentation.html.md +60 -0
  57. data/docs/source/articles/create-a-custom-discount.html.md +234 -0
  58. data/docs/source/articles/create-a-new-app.html.md +131 -0
  59. data/docs/source/articles/create-a-plugin.html.md +19 -0
  60. data/docs/source/articles/create-a-style-guide.html.md +71 -0
  61. data/docs/source/articles/create-a-theme.html.md +134 -0
  62. data/docs/source/articles/css-architectural-overview.html.md +89 -0
  63. data/docs/source/articles/customize-a-helper.html.md +91 -0
  64. data/docs/source/articles/decoration.html.md +415 -0
  65. data/docs/source/articles/define-and-configure-inventory-policies.html.md +107 -0
  66. data/docs/source/articles/documentation-style-guide.html.md +48 -0
  67. data/docs/source/articles/documentation.html.md +54 -0
  68. data/docs/source/articles/domain-modeling.html.md +82 -0
  69. data/docs/source/articles/error-pages.html.md.erb +95 -0
  70. data/docs/source/articles/extension-overview.html.md +152 -0
  71. data/docs/source/articles/favicon-support.html.md +112 -0
  72. data/docs/source/articles/feature-spec-helper-stylesheet.html.md +25 -0
  73. data/docs/source/articles/featurejs-and-feature-spec-helper.html.md +20 -0
  74. data/docs/source/articles/help-and-support.html.md +34 -0
  75. data/docs/source/articles/html-fragment-caching.html.md +46 -0
  76. data/docs/source/articles/http-caching.html.md +43 -0
  77. data/docs/source/articles/i18n.html.md +35 -0
  78. data/docs/source/articles/images-flow.html.md +10 -0
  79. data/docs/source/articles/index-storefront-search-documents.html.md +104 -0
  80. data/docs/source/articles/infrastructure.html.md +46 -0
  81. data/docs/source/articles/installing.html.md +61 -0
  82. data/docs/source/articles/integrate-a-payment-gateway.html.md +124 -0
  83. data/docs/source/articles/integrate-a-web-analytics-provider.html.md +35 -0
  84. data/docs/source/articles/integrate-an-inventory-management-system.html.md +88 -0
  85. data/docs/source/articles/integrating-with-other-software.html.md +59 -0
  86. data/docs/source/articles/inventory.html.md +352 -0
  87. data/docs/source/articles/javascript-coding-standards.html.md +30 -0
  88. data/docs/source/articles/javascript-modules.html.md +174 -0
  89. data/docs/source/articles/javascript-overview.html.md +62 -0
  90. data/docs/source/articles/javascript-reference-documentation.html.md +51 -0
  91. data/docs/source/articles/javascript-templates.html.md +52 -0
  92. data/docs/source/articles/low-level-caching.html.md +25 -0
  93. data/docs/source/articles/maintain-a-plugin.html.md +12 -0
  94. data/docs/source/articles/maintenance-policy.html.md +79 -0
  95. data/docs/source/articles/navigable.html.md +51 -0
  96. data/docs/source/articles/navigating-the-code.html.md +149 -0
  97. data/docs/source/articles/navigation.html.md +386 -0
  98. data/docs/source/articles/order-life-cycle.html.md +546 -0
  99. data/docs/source/articles/order-pricing.html.md +389 -0
  100. data/docs/source/articles/orders-and-items.html.md +210 -0
  101. data/docs/source/articles/orders.html.md +66 -0
  102. data/docs/source/articles/overriding.html.md +155 -0
  103. data/docs/source/articles/overview.html.md +43 -0
  104. data/docs/source/articles/plugins-overview.html.md +12 -0
  105. data/docs/source/articles/prerequisites-and-dependencies.html.md +202 -0
  106. data/docs/source/articles/products.html.md.erb +1270 -0
  107. data/docs/source/articles/progressive-web-application-support.html.md +148 -0
  108. data/docs/source/articles/rails-asset-manifests.html.md +33 -0
  109. data/docs/source/articles/rails-asset-view-helpers.html.md +25 -0
  110. data/docs/source/articles/reading-data.html.md +10 -0
  111. data/docs/source/articles/releasable.html.md +37 -0
  112. data/docs/source/articles/report-a-bug.html.md +75 -0
  113. data/docs/source/articles/ruby-coding-standards.html.md +10 -0
  114. data/docs/source/articles/run-sidekiq-in-a-local-environment.html.md +40 -0
  115. data/docs/source/articles/searching.html.md +1005 -0
  116. data/docs/source/articles/security-policy.html.md +42 -0
  117. data/docs/source/articles/seeds.html.md +345 -0
  118. data/docs/source/articles/shipping.html.md +756 -0
  119. data/docs/source/articles/sort-and-exclude-product-options.html.md +47 -0
  120. data/docs/source/articles/storefront-search-features.html.md +568 -0
  121. data/docs/source/articles/storefront-searches.html.md +126 -0
  122. data/docs/source/articles/style-guides.html.md +21 -0
  123. data/docs/source/articles/stylesheet-coding-standards.html.md +24 -0
  124. data/docs/source/articles/stylesheets-overview.html.md +67 -0
  125. data/docs/source/articles/swappable-list-data-structure.html.md +81 -0
  126. data/docs/source/articles/system-emails.html.md +102 -0
  127. data/docs/source/articles/taggable.html.md +8 -0
  128. data/docs/source/articles/test-a-credit-card-transaction.html.md +16 -0
  129. data/docs/source/articles/test-if-a-plugin-is-installed.html.md +34 -0
  130. data/docs/source/articles/testing.html.md +914 -0
  131. data/docs/source/articles/themes-overview.html.md +155 -0
  132. data/docs/source/articles/translate-administrable-content.html.md +14 -0
  133. data/docs/source/articles/translate-javascript-content.html.md +16 -0
  134. data/docs/source/articles/translate-or-customize-message-content.html.md +29 -0
  135. data/docs/source/articles/translate-or-customize-static-content.html.md +30 -0
  136. data/docs/source/articles/use-an-existing-workarea-app.html.md +108 -0
  137. data/docs/source/articles/view-models.html.md +509 -0
  138. data/docs/source/articles/views.html.md +14 -0
  139. data/docs/source/articles/workers.html.md +613 -0
  140. data/docs/source/articles/writing-data.html.md +10 -0
  141. data/docs/source/cli.html.md +163 -0
  142. data/docs/source/favicon.ico +0 -0
  143. data/docs/source/images/3-variants-1-option.png +0 -0
  144. data/docs/source/images/3-variants-3-options.png +0 -0
  145. data/docs/source/images/3-years-primary-image.png +0 -0
  146. data/docs/source/images/404-storefront-error-page.png +0 -0
  147. data/docs/source/images/404-system-content-admin.png +0 -0
  148. data/docs/source/images/404.jpg +0 -0
  149. data/docs/source/images/5-years-primary-image.png +0 -0
  150. data/docs/source/images/activity-dashboard.png +0 -0
  151. data/docs/source/images/activity-for-object.png +0 -0
  152. data/docs/source/images/activity-ui.png +0 -0
  153. data/docs/source/images/adding-captioned-image-block-custom-icon.png +0 -0
  154. data/docs/source/images/adding-captioned-image-block-default-icon.png +0 -0
  155. data/docs/source/images/admin-alerts-ui.png +0 -0
  156. data/docs/source/images/admin-category-range-filters.png +0 -0
  157. data/docs/source/images/admin-for-3-column-hero.png +0 -0
  158. data/docs/source/images/admin-help-index.png +0 -0
  159. data/docs/source/images/admin-help-ui.png +0 -0
  160. data/docs/source/images/admin-javascript.png +0 -0
  161. data/docs/source/images/admin-notification-for-deactivated-discount.png +0 -0
  162. data/docs/source/images/admin-notifications-ui.png +0 -0
  163. data/docs/source/images/admin-product-show-page.png +0 -0
  164. data/docs/source/images/admin-products-index-page.png +0 -0
  165. data/docs/source/images/admin-range-filters.png +0 -0
  166. data/docs/source/images/admin-style-guides-navigation.png +0 -0
  167. data/docs/source/images/after-re-seeding.png +0 -0
  168. data/docs/source/images/after-seeding-localhost-3000.png +0 -0
  169. data/docs/source/images/after-seeding.png +0 -0
  170. data/docs/source/images/arrow.svg +1 -0
  171. data/docs/source/images/arrow_white.svg +1 -0
  172. data/docs/source/images/aws-resource-map.png +0 -0
  173. data/docs/source/images/backordered-until-output-on-inventory-sku-card.png +0 -0
  174. data/docs/source/images/before-seeding-localhost-3000.png +0 -0
  175. data/docs/source/images/before-seeding.png +0 -0
  176. data/docs/source/images/browsing-workarea-versions-on-the-web.png +0 -0
  177. data/docs/source/images/bulk-asset-upload-on-assets-index-page.png +0 -0
  178. data/docs/source/images/bulk-asset-upload-while-editing-content.png +0 -0
  179. data/docs/source/images/bundle-show-workarea-core.png +0 -0
  180. data/docs/source/images/bundle-show-workarea.png +0 -0
  181. data/docs/source/images/calendar-for-backordered-until-field.png +0 -0
  182. data/docs/source/images/captioned-image-block-in-storefront.png +0 -0
  183. data/docs/source/images/captioned-image-content-block-storefront-component-style-guide.png +0 -0
  184. data/docs/source/images/cart-system-content-in-admin.png +0 -0
  185. data/docs/source/images/cart-system-content-in-storefront.png +0 -0
  186. data/docs/source/images/checkout-addresses-guest.png +0 -0
  187. data/docs/source/images/checkout-addresses-user.png +0 -0
  188. data/docs/source/images/checkout-confirmation.png +0 -0
  189. data/docs/source/images/checkout-flow-0.png +0 -0
  190. data/docs/source/images/checkout-flow-1.png +0 -0
  191. data/docs/source/images/checkout-flow-2.png +0 -0
  192. data/docs/source/images/checkout-flow-3.png +0 -0
  193. data/docs/source/images/checkout-flow-4.png +0 -0
  194. data/docs/source/images/checkout-payment-guest.png +0 -0
  195. data/docs/source/images/checkout-payment-user.png +0 -0
  196. data/docs/source/images/checkout-shipping.png +0 -0
  197. data/docs/source/images/color-picker-component-admin-style-guide.png +0 -0
  198. data/docs/source/images/color-picker-component-on-content-editing-screen.png +0 -0
  199. data/docs/source/images/commerce-model-carts-orders.png +0 -0
  200. data/docs/source/images/commerce-model-order-pricing.png +0 -0
  201. data/docs/source/images/commerce-model.png +0 -0
  202. data/docs/source/images/configuring-an-index-pattern-in-kibana.png +0 -0
  203. data/docs/source/images/content-block-presets.png +0 -0
  204. data/docs/source/images/content-search-customization.png +0 -0
  205. data/docs/source/images/country-with-region-data-in-address-form.png +0 -0
  206. data/docs/source/images/country-without-region-data-in-address-form.png +0 -0
  207. data/docs/source/images/create-content-block-preset-ui.png +0 -0
  208. data/docs/source/images/credit-card-icons.png +0 -0
  209. data/docs/source/images/css-added-through-admin.png +0 -0
  210. data/docs/source/images/css-admin-ui.png +0 -0
  211. data/docs/source/images/current-configuration-shown-in-admin-settings.png +0 -0
  212. data/docs/source/images/customer-impersonation-in-admin.png +0 -0
  213. data/docs/source/images/customer-impersonation-in-store-front.png +0 -0
  214. data/docs/source/images/date-filter-same-day.png +0 -0
  215. data/docs/source/images/developer-toolbar-in-store-front.png +0 -0
  216. data/docs/source/images/discounts-sorted-by-most-redeemed.png +0 -0
  217. data/docs/source/images/edit-help-article.png +0 -0
  218. data/docs/source/images/editing-content-for-search-customization.png +0 -0
  219. data/docs/source/images/editing-dynamic-captioned-image-block.png +0 -0
  220. data/docs/source/images/editing-product-fields-in-the-admin.png +0 -0
  221. data/docs/source/images/editing-search-system-content.png +0 -0
  222. data/docs/source/images/editing-static-captioned-image-block-custom-icon.png +0 -0
  223. data/docs/source/images/editing-static-captioned-image-block-default-icon.png +0 -0
  224. data/docs/source/images/external.svg +1 -0
  225. data/docs/source/images/favicon_16.png +0 -0
  226. data/docs/source/images/favicon_180.png +0 -0
  227. data/docs/source/images/favicon_32.png +0 -0
  228. data/docs/source/images/filters-all.png +0 -0
  229. data/docs/source/images/filters-control.png +0 -0
  230. data/docs/source/images/filters-custom.png +0 -0
  231. data/docs/source/images/filters-groups.png +0 -0
  232. data/docs/source/images/filters-material.png +0 -0
  233. data/docs/source/images/filters-omitted.png +0 -0
  234. data/docs/source/images/filters-pinned.png +0 -0
  235. data/docs/source/images/filters-range.png +0 -0
  236. data/docs/source/images/filters-sorted.png +0 -0
  237. data/docs/source/images/filters-wrapping-to-second-line-in-admin.png +0 -0
  238. data/docs/source/images/generic-product-template-images-no-options-selected.png +0 -0
  239. data/docs/source/images/generic-product-template-images-options-selected.png +0 -0
  240. data/docs/source/images/generic-template.png +0 -0
  241. data/docs/source/images/hosting.svg +1 -0
  242. data/docs/source/images/image-group-content-block-in-storefront.png +0 -0
  243. data/docs/source/images/images.svg +1 -0
  244. data/docs/source/images/import-export-screenshot.png +0 -0
  245. data/docs/source/images/invalid-display.png +0 -0
  246. data/docs/source/images/itcss.png +0 -0
  247. data/docs/source/images/kibana-dev-tools-console.png +0 -0
  248. data/docs/source/images/layout-content-admin-with-2-areas.png +0 -0
  249. data/docs/source/images/layout-content-admin-with-3-areas.png +0 -0
  250. data/docs/source/images/link-to-search-system-content.png +0 -0
  251. data/docs/source/images/logo.svg +1 -0
  252. data/docs/source/images/menu.svg +2 -0
  253. data/docs/source/images/mongo-replica-set.svg +1 -0
  254. data/docs/source/images/multi-column-hero-blocks.png +0 -0
  255. data/docs/source/images/option-selects-product-template-images-options-selected.png +0 -0
  256. data/docs/source/images/option-selects-template.png +0 -0
  257. data/docs/source/images/option-thumbnails-template.png +0 -0
  258. data/docs/source/images/order-item-total-price-diagram.png +0 -0
  259. data/docs/source/images/order-pricing-cart-example.png +0 -0
  260. data/docs/source/images/order-pricing-example-adjustments.png +0 -0
  261. data/docs/source/images/order-pricing-example-totals.png +0 -0
  262. data/docs/source/images/order-pricing-placed-order-example.png +0 -0
  263. data/docs/source/images/order-shipping-total-diagram.png +0 -0
  264. data/docs/source/images/order-show-with-multiple-tenders.png +0 -0
  265. data/docs/source/images/order-subtotal-price-diagram.png +0 -0
  266. data/docs/source/images/order-tax-total-diagram.png +0 -0
  267. data/docs/source/images/order-total-price-diagram.png +0 -0
  268. data/docs/source/images/order-total-value-diagram.png +0 -0
  269. data/docs/source/images/orders-dashboard-links.png +0 -0
  270. data/docs/source/images/oval.svg +1 -0
  271. data/docs/source/images/payment-icon-storefront-style-guide.png +0 -0
  272. data/docs/source/images/people-dashboard-links.png +0 -0
  273. data/docs/source/images/price-adjustments-diagram.png +0 -0
  274. data/docs/source/images/price-display-no-options.png +0 -0
  275. data/docs/source/images/price-display-options-selected.png +0 -0
  276. data/docs/source/images/pricing-calculators-diagram.png +0 -0
  277. data/docs/source/images/product-list-content-block-admin.png +0 -0
  278. data/docs/source/images/product-list-content-block-in-store-front.png +0 -0
  279. data/docs/source/images/promo-products-excluded-autocomplete-results-after.png +0 -0
  280. data/docs/source/images/promo-products-excluded-featured-category-results-after.png +0 -0
  281. data/docs/source/images/promo-products-excluded-recommendations-results-after.png +0 -0
  282. data/docs/source/images/promo-products-excluded-search-category-results-after.png +0 -0
  283. data/docs/source/images/promo-products-excluded-search-results-after.png +0 -0
  284. data/docs/source/images/promo-products-included-autocomplete-results-before.png +0 -0
  285. data/docs/source/images/promo-products-included-featured-category-results-before.png +0 -0
  286. data/docs/source/images/promo-products-included-recommendations-results-before.png +0 -0
  287. data/docs/source/images/promo-products-included-search-category-results-before.png +0 -0
  288. data/docs/source/images/promo-products-included-search-results-before.png +0 -0
  289. data/docs/source/images/rails-version-constraint.png +0 -0
  290. data/docs/source/images/re-enable-discount.png +0 -0
  291. data/docs/source/images/reading-data.svg +1 -0
  292. data/docs/source/images/readme-hero.png +0 -0
  293. data/docs/source/images/redesigned-customized-sort-for-search-results.png +0 -0
  294. data/docs/source/images/reviews-summary-above-share-buttons.png +0 -0
  295. data/docs/source/images/reviews-summary-below-product-name.png +0 -0
  296. data/docs/source/images/reviews-summary-below-share-buttons.png +0 -0
  297. data/docs/source/images/reviews-summary-removed.png +0 -0
  298. data/docs/source/images/rsa-fingerprint-for-stash.png +0 -0
  299. data/docs/source/images/ruby-version-constraint.png +0 -0
  300. data/docs/source/images/script-tag-added-through-admin.png +0 -0
  301. data/docs/source/images/search-analysis-admin-alternate-rendering.png +0 -0
  302. data/docs/source/images/search-analysis-admin.png +0 -0
  303. data/docs/source/images/search-quality-report.png +0 -0
  304. data/docs/source/images/search.svg +1 -0
  305. data/docs/source/images/searching-for-cart-system-content-in-admin.png +0 -0
  306. data/docs/source/images/searching-for-layout-system-content-in-admin.png +0 -0
  307. data/docs/source/images/seeded-admin.png +0 -0
  308. data/docs/source/images/seeds-from-plugins.png +0 -0
  309. data/docs/source/images/seo-metadata-automation-ui.png +0 -0
  310. data/docs/source/images/show-password-button.png +0 -0
  311. data/docs/source/images/storefront-autocomplete.png +0 -0
  312. data/docs/source/images/storefront-category-summary-content-block.png +0 -0
  313. data/docs/source/images/storefront-category.png +0 -0
  314. data/docs/source/images/storefront-product-after-overriding.png +0 -0
  315. data/docs/source/images/storefront-product-before-overriding.png +0 -0
  316. data/docs/source/images/storefront-product-browse-page.png +0 -0
  317. data/docs/source/images/storefront-product-recommendations.png +0 -0
  318. data/docs/source/images/storefront-product-show-page.png +0 -0
  319. data/docs/source/images/storefront-requests-and-search-requests.png +0 -0
  320. data/docs/source/images/storefront-search-request-handling.png +0 -0
  321. data/docs/source/images/storefront-search-response-creation.png +0 -0
  322. data/docs/source/images/storefront-search.png +0 -0
  323. data/docs/source/images/storefront-style-guides-navigation.png +0 -0
  324. data/docs/source/images/styles.css +3 -0
  325. data/docs/source/images/tax-categories-ui.png +0 -0
  326. data/docs/source/images/tax-rates-ui.png +0 -0
  327. data/docs/source/images/unpurchasable-product.png +0 -0
  328. data/docs/source/images/url-redirects-filtering.png +0 -0
  329. data/docs/source/images/utility-nav-area-in-admin.png +0 -0
  330. data/docs/source/images/utility-nav-area-in-storefront.png +0 -0
  331. data/docs/source/images/validation-message-in-storefront.png +0 -0
  332. data/docs/source/images/view-model-interface.png +0 -0
  333. data/docs/source/images/viewing-workarea-version-in-source.png +0 -0
  334. data/docs/source/images/workarea.svg +1 -0
  335. data/docs/source/images/worst-performing-searches-on-results-customization-page.png +0 -0
  336. data/docs/source/images/writing-data.svg +1 -0
  337. data/docs/source/index.html.erb +167 -0
  338. data/docs/source/javascripts/jquery.js +2 -0
  339. data/docs/source/javascripts/lunr.js +7 -0
  340. data/docs/source/javascripts/site.js +299 -0
  341. data/docs/source/javascripts/vendor/highlight.pack.js +2 -0
  342. data/docs/source/layouts/article.erb +106 -0
  343. data/docs/source/layouts/bare.erb +46 -0
  344. data/docs/source/layouts/layout.erb +43 -0
  345. data/docs/source/release-notes.html.md +258 -0
  346. data/docs/source/release-notes/workarea-3-0-0.html.md +146 -0
  347. data/docs/source/release-notes/workarea-3-0-1.html.md +161 -0
  348. data/docs/source/release-notes/workarea-3-0-10.html.md +39 -0
  349. data/docs/source/release-notes/workarea-3-0-11.html.md +277 -0
  350. data/docs/source/release-notes/workarea-3-0-12.html.md +14 -0
  351. data/docs/source/release-notes/workarea-3-0-13.html.md +153 -0
  352. data/docs/source/release-notes/workarea-3-0-14.html.md +93 -0
  353. data/docs/source/release-notes/workarea-3-0-15.html.md +107 -0
  354. data/docs/source/release-notes/workarea-3-0-16.html.md +36 -0
  355. data/docs/source/release-notes/workarea-3-0-17.html.md +141 -0
  356. data/docs/source/release-notes/workarea-3-0-18.html.md +123 -0
  357. data/docs/source/release-notes/workarea-3-0-19.html.md +160 -0
  358. data/docs/source/release-notes/workarea-3-0-2.html.md +222 -0
  359. data/docs/source/release-notes/workarea-3-0-20.html.md +95 -0
  360. data/docs/source/release-notes/workarea-3-0-21.html.md +168 -0
  361. data/docs/source/release-notes/workarea-3-0-22.html.md +268 -0
  362. data/docs/source/release-notes/workarea-3-0-23.html.md +173 -0
  363. data/docs/source/release-notes/workarea-3-0-24.html.md +19 -0
  364. data/docs/source/release-notes/workarea-3-0-25.html.md +26 -0
  365. data/docs/source/release-notes/workarea-3-0-26.html.md +199 -0
  366. data/docs/source/release-notes/workarea-3-0-27.html.md +113 -0
  367. data/docs/source/release-notes/workarea-3-0-28.html.md +39 -0
  368. data/docs/source/release-notes/workarea-3-0-29.html.md +73 -0
  369. data/docs/source/release-notes/workarea-3-0-3.html.md +35 -0
  370. data/docs/source/release-notes/workarea-3-0-30.html.md +186 -0
  371. data/docs/source/release-notes/workarea-3-0-31.html.md +125 -0
  372. data/docs/source/release-notes/workarea-3-0-32.html.md +73 -0
  373. data/docs/source/release-notes/workarea-3-0-33.html.md +137 -0
  374. data/docs/source/release-notes/workarea-3-0-34.html.md +203 -0
  375. data/docs/source/release-notes/workarea-3-0-35.html.md +205 -0
  376. data/docs/source/release-notes/workarea-3-0-36.html.md +105 -0
  377. data/docs/source/release-notes/workarea-3-0-37.html.md +144 -0
  378. data/docs/source/release-notes/workarea-3-0-38.html.md +73 -0
  379. data/docs/source/release-notes/workarea-3-0-39.html.md +77 -0
  380. data/docs/source/release-notes/workarea-3-0-4.html.md +14 -0
  381. data/docs/source/release-notes/workarea-3-0-40.html.md +130 -0
  382. data/docs/source/release-notes/workarea-3-0-41.html.md +70 -0
  383. data/docs/source/release-notes/workarea-3-0-42.html.md +52 -0
  384. data/docs/source/release-notes/workarea-3-0-43.html.md +72 -0
  385. data/docs/source/release-notes/workarea-3-0-44.html.md +93 -0
  386. data/docs/source/release-notes/workarea-3-0-45.html.md +61 -0
  387. data/docs/source/release-notes/workarea-3-0-46.html.md +171 -0
  388. data/docs/source/release-notes/workarea-3-0-47.html.md +130 -0
  389. data/docs/source/release-notes/workarea-3-0-48.html.md +160 -0
  390. data/docs/source/release-notes/workarea-3-0-49.html.md +28 -0
  391. data/docs/source/release-notes/workarea-3-0-5.html.md +225 -0
  392. data/docs/source/release-notes/workarea-3-0-50.html.md +74 -0
  393. data/docs/source/release-notes/workarea-3-0-51.html.md +61 -0
  394. data/docs/source/release-notes/workarea-3-0-52.html.md +76 -0
  395. data/docs/source/release-notes/workarea-3-0-53.html.md +126 -0
  396. data/docs/source/release-notes/workarea-3-0-54.html.md +112 -0
  397. data/docs/source/release-notes/workarea-3-0-55.html.md +105 -0
  398. data/docs/source/release-notes/workarea-3-0-56.html.md +56 -0
  399. data/docs/source/release-notes/workarea-3-0-57.html.md +82 -0
  400. data/docs/source/release-notes/workarea-3-0-58.html.md +153 -0
  401. data/docs/source/release-notes/workarea-3-0-59.html.md +78 -0
  402. data/docs/source/release-notes/workarea-3-0-6.html.md +165 -0
  403. data/docs/source/release-notes/workarea-3-0-60.html.md +43 -0
  404. data/docs/source/release-notes/workarea-3-0-61.html.md +46 -0
  405. data/docs/source/release-notes/workarea-3-0-62.html.md +23 -0
  406. data/docs/source/release-notes/workarea-3-0-63.html.md +25 -0
  407. data/docs/source/release-notes/workarea-3-0-64.html.md +25 -0
  408. data/docs/source/release-notes/workarea-3-0-65.html.md +37 -0
  409. data/docs/source/release-notes/workarea-3-0-7.html.md +207 -0
  410. data/docs/source/release-notes/workarea-3-0-8.html.md +337 -0
  411. data/docs/source/release-notes/workarea-3-0-9.html.md +196 -0
  412. data/docs/source/release-notes/workarea-3-1-0.html.md +414 -0
  413. data/docs/source/release-notes/workarea-3-1-1.html.md +139 -0
  414. data/docs/source/release-notes/workarea-3-1-10.html.md +19 -0
  415. data/docs/source/release-notes/workarea-3-1-11.html.md +27 -0
  416. data/docs/source/release-notes/workarea-3-1-12.html.md +216 -0
  417. data/docs/source/release-notes/workarea-3-1-13.html.md +113 -0
  418. data/docs/source/release-notes/workarea-3-1-14.html.md +39 -0
  419. data/docs/source/release-notes/workarea-3-1-15.html.md +107 -0
  420. data/docs/source/release-notes/workarea-3-1-16.html.md +188 -0
  421. data/docs/source/release-notes/workarea-3-1-17.html.md +141 -0
  422. data/docs/source/release-notes/workarea-3-1-18.html.md +73 -0
  423. data/docs/source/release-notes/workarea-3-1-19.html.md +137 -0
  424. data/docs/source/release-notes/workarea-3-1-2.html.md +55 -0
  425. data/docs/source/release-notes/workarea-3-1-20.html.md +203 -0
  426. data/docs/source/release-notes/workarea-3-1-21.html.md +205 -0
  427. data/docs/source/release-notes/workarea-3-1-22.html.md +121 -0
  428. data/docs/source/release-notes/workarea-3-1-23.html.md +144 -0
  429. data/docs/source/release-notes/workarea-3-1-24.html.md +94 -0
  430. data/docs/source/release-notes/workarea-3-1-25.html.md +77 -0
  431. data/docs/source/release-notes/workarea-3-1-26.html.md +130 -0
  432. data/docs/source/release-notes/workarea-3-1-27.html.md +70 -0
  433. data/docs/source/release-notes/workarea-3-1-28.html.md +52 -0
  434. data/docs/source/release-notes/workarea-3-1-29.html.md +44 -0
  435. data/docs/source/release-notes/workarea-3-1-3.html.md +185 -0
  436. data/docs/source/release-notes/workarea-3-1-30.html.md +72 -0
  437. data/docs/source/release-notes/workarea-3-1-31.html.md +93 -0
  438. data/docs/source/release-notes/workarea-3-1-32.html.md +61 -0
  439. data/docs/source/release-notes/workarea-3-1-33.html.md +171 -0
  440. data/docs/source/release-notes/workarea-3-1-34.html.md +130 -0
  441. data/docs/source/release-notes/workarea-3-1-35.html.md +179 -0
  442. data/docs/source/release-notes/workarea-3-1-36.html.md +28 -0
  443. data/docs/source/release-notes/workarea-3-1-37.html.md +74 -0
  444. data/docs/source/release-notes/workarea-3-1-38.html.md +61 -0
  445. data/docs/source/release-notes/workarea-3-1-39.html.md +96 -0
  446. data/docs/source/release-notes/workarea-3-1-4.html.md +148 -0
  447. data/docs/source/release-notes/workarea-3-1-40.html.md +126 -0
  448. data/docs/source/release-notes/workarea-3-1-41.html.md +128 -0
  449. data/docs/source/release-notes/workarea-3-1-42.html.md +105 -0
  450. data/docs/source/release-notes/workarea-3-1-43.html.md +37 -0
  451. data/docs/source/release-notes/workarea-3-1-44.html.md +82 -0
  452. data/docs/source/release-notes/workarea-3-1-45.html.md +153 -0
  453. data/docs/source/release-notes/workarea-3-1-46.html.md +91 -0
  454. data/docs/source/release-notes/workarea-3-1-47.html.md +65 -0
  455. data/docs/source/release-notes/workarea-3-1-48.html.md +46 -0
  456. data/docs/source/release-notes/workarea-3-1-49.html.md +23 -0
  457. data/docs/source/release-notes/workarea-3-1-5.html.md +169 -0
  458. data/docs/source/release-notes/workarea-3-1-50.html.md +42 -0
  459. data/docs/source/release-notes/workarea-3-1-51.html.md +25 -0
  460. data/docs/source/release-notes/workarea-3-1-52.html.md +57 -0
  461. data/docs/source/release-notes/workarea-3-1-6.html.md +117 -0
  462. data/docs/source/release-notes/workarea-3-1-7.html.md +176 -0
  463. data/docs/source/release-notes/workarea-3-1-8.html.md +283 -0
  464. data/docs/source/release-notes/workarea-3-1-9.html.md +212 -0
  465. data/docs/source/release-notes/workarea-3-2-0.html.md +1705 -0
  466. data/docs/source/release-notes/workarea-3-2-1.html.md +216 -0
  467. data/docs/source/release-notes/workarea-3-2-10.html.md +237 -0
  468. data/docs/source/release-notes/workarea-3-2-11.html.md +121 -0
  469. data/docs/source/release-notes/workarea-3-2-12.html.md +145 -0
  470. data/docs/source/release-notes/workarea-3-2-13.html.md +138 -0
  471. data/docs/source/release-notes/workarea-3-2-14.html.md +77 -0
  472. data/docs/source/release-notes/workarea-3-2-15.html.md +130 -0
  473. data/docs/source/release-notes/workarea-3-2-16.html.md +111 -0
  474. data/docs/source/release-notes/workarea-3-2-17.html.md +52 -0
  475. data/docs/source/release-notes/workarea-3-2-18.html.md +44 -0
  476. data/docs/source/release-notes/workarea-3-2-19.html.md +72 -0
  477. data/docs/source/release-notes/workarea-3-2-2.html.md +145 -0
  478. data/docs/source/release-notes/workarea-3-2-20.html.md +93 -0
  479. data/docs/source/release-notes/workarea-3-2-21.html.md +61 -0
  480. data/docs/source/release-notes/workarea-3-2-22.html.md +154 -0
  481. data/docs/source/release-notes/workarea-3-2-23.html.md +130 -0
  482. data/docs/source/release-notes/workarea-3-2-24.html.md +200 -0
  483. data/docs/source/release-notes/workarea-3-2-25.html.md +28 -0
  484. data/docs/source/release-notes/workarea-3-2-26.html.md +94 -0
  485. data/docs/source/release-notes/workarea-3-2-27.html.md +61 -0
  486. data/docs/source/release-notes/workarea-3-2-28.html.md +96 -0
  487. data/docs/source/release-notes/workarea-3-2-29.html.md +126 -0
  488. data/docs/source/release-notes/workarea-3-2-30.html.md +112 -0
  489. data/docs/source/release-notes/workarea-3-2-31.html.md +105 -0
  490. data/docs/source/release-notes/workarea-3-2-32.html.md +56 -0
  491. data/docs/source/release-notes/workarea-3-2-33.html.md +82 -0
  492. data/docs/source/release-notes/workarea-3-2-34.html.md +153 -0
  493. data/docs/source/release-notes/workarea-3-2-35.html.md +91 -0
  494. data/docs/source/release-notes/workarea-3-2-36.html.md +118 -0
  495. data/docs/source/release-notes/workarea-3-2-37.html.md +46 -0
  496. data/docs/source/release-notes/workarea-3-2-38.html.md +23 -0
  497. data/docs/source/release-notes/workarea-3-2-39.html.md +42 -0
  498. data/docs/source/release-notes/workarea-3-2-4.html.md +109 -0
  499. data/docs/source/release-notes/workarea-3-2-40.html.md +25 -0
  500. data/docs/source/release-notes/workarea-3-2-41.html.md +90 -0
  501. data/docs/source/release-notes/workarea-3-2-5.html.md +186 -0
  502. data/docs/source/release-notes/workarea-3-2-6.html.md +173 -0
  503. data/docs/source/release-notes/workarea-3-2-7.html.md +89 -0
  504. data/docs/source/release-notes/workarea-3-2-8.html.md +137 -0
  505. data/docs/source/release-notes/workarea-3-2-9.html.md +219 -0
  506. data/docs/source/release-notes/workarea-3-3-0.html.md +1272 -0
  507. data/docs/source/release-notes/workarea-3-3-1.html.md +324 -0
  508. data/docs/source/release-notes/workarea-3-3-10.html.md +69 -0
  509. data/docs/source/release-notes/workarea-3-3-11.html.md +72 -0
  510. data/docs/source/release-notes/workarea-3-3-12.html.md +136 -0
  511. data/docs/source/release-notes/workarea-3-3-13.html.md +61 -0
  512. data/docs/source/release-notes/workarea-3-3-14.html.md +196 -0
  513. data/docs/source/release-notes/workarea-3-3-15.html.md +167 -0
  514. data/docs/source/release-notes/workarea-3-3-16.html.md +234 -0
  515. data/docs/source/release-notes/workarea-3-3-17.html.md +82 -0
  516. data/docs/source/release-notes/workarea-3-3-18.html.md +165 -0
  517. data/docs/source/release-notes/workarea-3-3-19.html.md +106 -0
  518. data/docs/source/release-notes/workarea-3-3-2.html.md +72 -0
  519. data/docs/source/release-notes/workarea-3-3-20.html.md +116 -0
  520. data/docs/source/release-notes/workarea-3-3-21.html.md +228 -0
  521. data/docs/source/release-notes/workarea-3-3-22.html.md +125 -0
  522. data/docs/source/release-notes/workarea-3-3-23.html.md +154 -0
  523. data/docs/source/release-notes/workarea-3-3-24.html.md +70 -0
  524. data/docs/source/release-notes/workarea-3-3-25.html.md +114 -0
  525. data/docs/source/release-notes/workarea-3-3-26.html.md +260 -0
  526. data/docs/source/release-notes/workarea-3-3-27.html.md +138 -0
  527. data/docs/source/release-notes/workarea-3-3-28.html.md +147 -0
  528. data/docs/source/release-notes/workarea-3-3-29.html.md +63 -0
  529. data/docs/source/release-notes/workarea-3-3-3.html.md +153 -0
  530. data/docs/source/release-notes/workarea-3-3-30.html.md +102 -0
  531. data/docs/source/release-notes/workarea-3-3-31.html.md +57 -0
  532. data/docs/source/release-notes/workarea-3-3-32.html.md +44 -0
  533. data/docs/source/release-notes/workarea-3-3-33.html.md +114 -0
  534. data/docs/source/release-notes/workarea-3-3-4.html.md +332 -0
  535. data/docs/source/release-notes/workarea-3-3-5.html.md +242 -0
  536. data/docs/source/release-notes/workarea-3-3-6.html.md +100 -0
  537. data/docs/source/release-notes/workarea-3-3-7.html.md +148 -0
  538. data/docs/source/release-notes/workarea-3-3-8.html.md +163 -0
  539. data/docs/source/release-notes/workarea-3-3-9.html.md +93 -0
  540. data/docs/source/release-notes/workarea-3-4-0.html.md +580 -0
  541. data/docs/source/release-notes/workarea-3-4-1.html.md +150 -0
  542. data/docs/source/release-notes/workarea-3-4-10.html.md +72 -0
  543. data/docs/source/release-notes/workarea-3-4-11.html.md +60 -0
  544. data/docs/source/release-notes/workarea-3-4-12.html.md +155 -0
  545. data/docs/source/release-notes/workarea-3-4-2.html.md +188 -0
  546. data/docs/source/release-notes/workarea-3-4-3.html.md +136 -0
  547. data/docs/source/release-notes/workarea-3-4-4.html.md +114 -0
  548. data/docs/source/release-notes/workarea-3-4-5.html.md +275 -0
  549. data/docs/source/release-notes/workarea-3-4-6.html.md +169 -0
  550. data/docs/source/release-notes/workarea-3-4-7.html.md +162 -0
  551. data/docs/source/release-notes/workarea-3-4-8.html.md +95 -0
  552. data/docs/source/release-notes/workarea-3-4-9.html.md +135 -0
  553. data/docs/source/search.html.erb +34 -0
  554. data/docs/source/shared/_header.erb +61 -0
  555. data/docs/source/shared/_svgs.erb +17 -0
  556. data/docs/source/style_guide/index.html.erb +382 -0
  557. data/docs/source/stylesheets/_base.scss +125 -0
  558. data/docs/source/stylesheets/_components.scss +669 -0
  559. data/docs/source/stylesheets/_helpers.scss +10 -0
  560. data/docs/source/stylesheets/_opinions.scss +42 -0
  561. data/docs/source/stylesheets/_settings.scss +56 -0
  562. data/docs/source/stylesheets/_typography.scss +119 -0
  563. data/docs/source/stylesheets/site.css.scss +14 -0
  564. data/docs/source/stylesheets/vendor/_avalanche.scss +328 -0
  565. data/docs/source/stylesheets/vendor/_normalize.scss +341 -0
  566. data/docs/source/stylesheets/vendor/highlight/_tomorrow_night_blue.scss +75 -0
  567. data/docs/source/upgrade-guides.html.md +18 -0
  568. data/docs/source/upgrade-guides/workarea-3-4-0.html.md +152 -0
  569. data/docs/workarea_renderer.rb +8 -0
  570. data/docs/yarn.lock +2522 -0
  571. metadata +669 -0
@@ -0,0 +1,42 @@
1
+ ---
2
+ title: Security Policy
3
+ created_at: 2019/05/28
4
+ excerpt: Guidelines for security procedures and reporting vulnerabilities.
5
+ ---
6
+
7
+ # Security policy
8
+
9
+ Workarea takes web security very seriously. This means including features to protect application makers from common issues like CSRF, Script Injection, and the like. But it also means a clear policy on how to report vulnerabilities and receive updates when patches to those are released.
10
+
11
+ ## Supported versions
12
+
13
+ For all security issues, all releases in the current major series will receive patches and new versions. This is currently 3.4.x, 3.3.x, 3.2.x, 3.1.x, and 3.0.x. When a release series is no longer supported, it’s your own responsibility to deal with bugs and security issues. We may provide backports of the fixes and publish them to git, however there will be no new versions released. If you are not comfortable maintaining your own versions, you should upgrade to a supported version. The classification of a security issue is determined by the Workarea core team.
14
+
15
+ ## Reporting a vulnerability
16
+
17
+ All security bugs in Workarea should be reported to [security@workarea.com](mailto:security@workarea.com). Your report will be acknowledged within 24 hours, and you’ll receive a more detailed response within 48 hours indicating the next steps in handling your report.
18
+
19
+ After the initial reply to your report the security team will endeavor to keep you informed of the progress being made towards a fix and full announcement. These updates will be sent at least every five days, in reality this is more likely to be every 24-72 hours.
20
+
21
+ If you have not received a reply to your email within 72 hours, or have not heard from the security team for the past five days there are a few steps you can take:
22
+
23
+ 1. Send an email to the support team ([support@workarea.com](mailto:support@workarea.com)).
24
+ 2. Contact the core team through our [community Slack channel](https://workarea-community.slack.com)
25
+ 3. Reach out to a [member of the product team](https://github.com/workarea-commerce/workarea/blob/master/CONTRIBUTORS.md) directly.
26
+
27
+ ## Disclosure process
28
+
29
+ 1. Security report received and is assigned a primary handler. This person will coordinate the fix and release process. Problem is confirmed and a list of all affected versions is determined. Code is audited to find any potential similar problems.
30
+
31
+ 2. Fixes are prepared for all releases which are still supported. These fixes are not committed to the public repository but rather held locally pending the announcement.
32
+
33
+ 3. A suggested embargo date for this vulnerability is chosen.
34
+
35
+ 4. On the embargo date, the changes are pushed to the public repository and new gems released to rubygems. The Workarea security mailing list is sent a copy of the announcement, and a copy of the announcement will be published to the developer forum.
36
+
37
+ Typically the embargo date will be within 72 hours from when a fix has been identified, however this may vary depending on the severity of the bug or difficulty in applying a fix. This process can take some time, especially when coordination is required with maintainers of other projects. Every effort will be made to handle the bug in as timely a manner as possible, however it’s important that we follow the release process above to ensure that the disclosure is handled in a consistent manner.
38
+
39
+ ## Receiving disclosures
40
+
41
+ The best way to receive all the security announcements is to subscribe to the Workarea Security mailing list. The mailing list is very low traffic, and it receives the public notifications the moment the embargo is lifted. No one outside the core team and the initial reporter will be notified prior to the lifting of the embargo. We regret that we cannot make exceptions to this policy for high traffic or important sites, as any disclosure beyond the minimum required to coordinate a fix could cause an early leak of the vulnerability.
42
+ If you have any suggestions to improve this policy, please send an email to [security@workarea.com](mailto:security@workarea.com).
@@ -0,0 +1,345 @@
1
+ ---
2
+ title: Seeds
3
+ excerpt: Seeds are default data appropriate for developing, testing, or otherwise using a Workarea application, particularly in a development environment. Seeding is the process of writing the seeds within a particular environment.
4
+ ---
5
+
6
+ # Seeds
7
+
8
+ <dfn>Seeds</dfn> are default data appropriate for developing, testing, or otherwise using a Workarea application, particularly in a development environment. <dfn>Seeding</dfn> is the process of writing the seeds within a particular environment.
9
+
10
+ Be careful! **Seeding is a destructive process** that purges all existing MongoDB and Elasticsearch data before writing. Be certain you are willing to lose all data before running seeds in an environment.
11
+
12
+ Seeding is performed exclusively through a command line interface. To seed an environment, bundle the application and run the db:seed task:
13
+
14
+ ```bash
15
+ $ cd application_directory
16
+ $ bundle
17
+ $ bin/rails db:seed
18
+ ```
19
+
20
+ See below for a detailed example.
21
+
22
+ The base platform includes the seeds necessary to use a generic Workarea application. Plugins and applications can extend seeds as necessary to support platform customizations. (I'll cover seeds implementation and extension in detail in a future guide.)
23
+
24
+ ## Seeding a New Development Environment
25
+
26
+ To demonstrate seeds, consider the fictional Workarea application <cite>Boardgamz</cite>, newly created within a fresh [Workarea app](create-a-new-app.html).
27
+
28
+ ### Before Seeding
29
+
30
+ This environment has no MongoDB databases, collections, or documents, except for the default _test_ and _local_ databases:
31
+
32
+ ```bash
33
+ $ mongo --eval 'db.getMongo().getDBNames()'
34
+ MongoDB shell version: 3.2.5
35
+ connecting to: test
36
+ ["local"]
37
+ $
38
+ ```
39
+
40
+ And there are no Elasticsearch indexes or documents:
41
+
42
+ ```bash
43
+ $ curl localhost:9200/_cat/indices?v
44
+ health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
45
+ $
46
+ ```
47
+
48
+ You can start the application within the development environment, but you can't do much with it:
49
+
50
+ ![Before seeding](images/before-seeding.png)
51
+
52
+ There is no navigation and nothing to search for. There are no products, categories, or content to browse. The Admin is inaccessible because there are no users who can authenticate as administrators.
53
+
54
+ To use this application, you need data. If you're new to Workarea, you may be surprised how much data is required for a typical flow, such as the process of adding some products to the cart and checking out. This path requires at least a few model instances from each of the following business domains:
55
+
56
+ - Catalog
57
+ - Inventory
58
+ - Navigation
59
+ - Pricing
60
+ - Shipping
61
+ - Tax
62
+
63
+ Seeding provides an effective way to write this necessary data with little effort.
64
+
65
+ ### Seeding
66
+
67
+ The following example demonstrates seeding the environment. The output will display progressively as the process runs.
68
+
69
+ ```bash
70
+ $ bin/rails db:seed
71
+ == Setting up...
72
+ Deleting Elasticsearch indexes...
73
+ Cleaning MongoDB collections...
74
+ Flushing Redis database...
75
+ Ensuring MongoDB indexes...
76
+ Ensuring Elasticsearch indexes...
77
+
78
+ == Loading MongoDB data
79
+ Adding search settings...
80
+ Adding content emails...
81
+ Adding tax rates...
82
+ Adding shipping services...
83
+ Adding assets...
84
+ Adding categories...
85
+ Adding products...
86
+ Adding auxiliary pages...
87
+ Adding browsing pages...
88
+ Adding discounts...
89
+ Adding dynamic content...
90
+ Adding browsing navigation...
91
+ Adding customer service navigation...
92
+ Adding system content...
93
+ Adding admin users...
94
+ Adding customers...
95
+ Adding orders...
96
+ Adding inquiries...
97
+ Adding help articles...
98
+ Adding analytics...
99
+
100
+ == Loading Elasticsearch data
101
+ Indexing storefront...
102
+ Indexing admin...
103
+ Indexing help...
104
+
105
+ Success!
106
+ $
107
+ ```
108
+
109
+ ### After Seeding
110
+
111
+ After seeding, MongoDB contains a development database for the application
112
+
113
+ ```bash
114
+ $ mongo --eval 'db.getMongo().getDBNames()'
115
+ MongoDB shell version: 3.2.5
116
+ connecting to: test
117
+ ["boardgamz_development", "local"]
118
+ $
119
+ ```
120
+
121
+ that holds many collections
122
+
123
+ ```bash
124
+ $ mongo boardgamz_development --eval "db.getCollectionNames().length"
125
+ MongoDB shell version: 3.2.5
126
+ connecting to: boardgamz_development
127
+ 61
128
+ $
129
+ ```
130
+
131
+ some of which are listed below:
132
+
133
+ ```bash
134
+ $ mongo boardgamz_development --eval "db.getCollectionNames().slice(0,20)"
135
+ MongoDB shell version: 3.2.5
136
+ connecting to: boardgamz_development
137
+ [
138
+ "mongoid_audit_log_entries",
139
+ "workarea_analytics_categories",
140
+ "workarea_analytics_category_revenues",
141
+ "workarea_analytics_discount_revenues",
142
+ "workarea_analytics_discounts",
143
+ "workarea_analytics_discounts_summaries",
144
+ "workarea_analytics_filters",
145
+ "workarea_analytics_last_four_weeks_searches",
146
+ "workarea_analytics_navigations",
147
+ "workarea_analytics_new_customers",
148
+ "workarea_analytics_orders_summaries",
149
+ "workarea_analytics_product_revenues",
150
+ "workarea_analytics_products",
151
+ "workarea_analytics_search_abandonment_rates",
152
+ "workarea_analytics_searches",
153
+ "workarea_analytics_signups",
154
+ "workarea_analytics_users",
155
+ "workarea_bulk_actions",
156
+ "workarea_catalog_categories",
157
+ "workarea_catalog_product_placeholder_images"
158
+ ]
159
+ $
160
+ ```
161
+
162
+ Likewise, Elasticsearch contains multiple indexes for the application
163
+
164
+ ```bash
165
+ $ curl localhost:9200/_cat/indices?v
166
+ health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
167
+ yellow open boardgamz_development_en_storefront D38474KwRS6oYndzWtjnrQ 5 1 140 4 964.9kb 964.9kb
168
+ yellow open boardgamz_development_en_help j5M7K9QrT1a109VIujukcg 5 1 52 0 1.2mb 1.2mb
169
+ yellow open boardgamz_development_en_admin 3fTuI3raSjePJU1IJvmBTw 5 1 762 0 2mb 2mb
170
+ $
171
+ ```
172
+
173
+ which hold many documents:
174
+
175
+ ```bash
176
+ $ curl localhost:9200/_cat/count?v
177
+ epoch timestamp count
178
+ 1511905005 21:36:45 954
179
+ $
180
+ ```
181
+
182
+ The upshot of this is a usable Storefront, complete with products, categories, content, navigation, and other data:
183
+
184
+ ![After seeding](images/after-seeding.png)
185
+
186
+ ### Admin Access
187
+
188
+ Seeding also provides access to the Admin, which is displaying order and analytics data that was also seeded:
189
+
190
+ ![Seeded Admin](images/seeded-admin.png)
191
+
192
+ In a development environment, the following credentials provide super admin privileges (assuming you haven't customized your admin user seeds):
193
+
194
+ | Email | user@workarea.com |
195
+ |--|--|
196
+ | Password | W3bl1nc! |
197
+
198
+ ## Re-Seeding
199
+
200
+ The seeds included with Core use some random data, so seeding is not idempotent.&nbsp;<sup><a href="#notes" id="note-2-context">[1]</a></sup> However, re-seeding should produce data that is uniform—having the same general “shape”—each time.
201
+
202
+ To demonstrate, use Mongoid to query the number of products and the first product in the Boardgamz development database:
203
+
204
+ ```bash
205
+ $ bin/rails r 'puts Workarea::Catalog::Product.count'
206
+ 90
207
+ $ bin/rails r 'puts Workarea::Catalog::Product.first.name'
208
+ Heavy Duty Aluminum Knife
209
+ $
210
+ ```
211
+
212
+ Now, re-seed. (Also capture the seeding output to a file and add it to the git index—this is for a future example.)
213
+
214
+ ```bash
215
+ $ bin/rails db:seed > seeding_output
216
+ $ git add seeding_output
217
+ $
218
+ ```
219
+
220
+ After seeding completes, query the products again. You end up with the same _number_ of products, but the products themselves are different.
221
+
222
+ ```bash
223
+ $ bin/rails r 'puts Workarea::Catalog::Product.count'
224
+ 90
225
+ $ bin/rails r 'puts Workarea::Catalog::Product.first.name'
226
+ Aerodynamic Linen Keyboard
227
+ $
228
+ ```
229
+
230
+ As another example, the image below shows the Storefront after re-seeding. Comparing it to the Storefront image above, you can see the “Header Promo Body” is identical, while the navigation is similar but different:
231
+
232
+ ![After re-seeding](images/after-re-seeding.png)
233
+
234
+ The uniformity of seeds makes them valuable for the following additional use cases:
235
+
236
+ - Generally synchronizing your development environment data with other developers on your team
237
+ - Loading data to support new features or extensions added by you or other developers on your team
238
+ - Updating default data after installing or removing plugins from your application
239
+ - Restoring your application data to a known-good state
240
+
241
+ It is therefore sensible to re-seed your development environment in the following situations:
242
+
243
+ - After you've added a feature or extension which includes seeds (to ensure the seeds are adequate)
244
+ - After changing which plugins are installed
245
+ - After merging changes from another developer where either of the above is applicable
246
+ - When experiencing issues in development that may be data-related
247
+
248
+ ## Extending Seeds
249
+
250
+ As an application developer, you can define your own seeds. You can also [decorate](decoration.html) and [configure](configuration.html) existing seeds. However, these techniques depend on an understanding of how seeds are implemented, which I haven't covered yet.
251
+
252
+ Therefore, let's instead take a look at how your installed plugins affect your seeds. Plugins typically add _new_ seeds, which support the other extensions applied within the plugin. For example, the blog plugin seeds blogs, entries, and comments; while the clothing and package products plugins seed additional products.
253
+
254
+ To see this, add some plugins to the application, as shown in the following git patch:
255
+
256
+ ```diff
257
+ diff --git a/Gemfile b/Gemfile
258
+ index 0efec88..b3f9095 100644
259
+ --- a/Gemfile
260
+ +++ b/Gemfile
261
+ @@ -45,4 +45,11 @@ end
262
+
263
+ source 'https://gems.workarea.com' do
264
+ gem 'workarea', '3.1.5'
265
+ + gem 'workarea-blog', '3.1.1'
266
+ + gem 'workarea-clothing', '2.1.3'
267
+ + gem 'workarea-gift_cards', '3.2.1'
268
+ + gem 'workarea-package_products', '3.1.2'
269
+ + gem 'workarea-reviews', '2.1.0'
270
+ + gem 'workarea-store_locator', '4.0.0'
271
+ + gem 'workarea-wish_lists', '2.0.3'
272
+ end
273
+ ```
274
+
275
+ Bundle the app; then re-seed and capture the seeding output to the same file as above.
276
+
277
+ ```bash
278
+ $ bundle
279
+ $ bin/rails db:seed > seeding_output
280
+ ```
281
+
282
+ After seeding completes, diff the output file to see how the seeding output has changed after installing plugins:
283
+
284
+ ```bash
285
+ $ git diff seeding_output
286
+ ```
287
+
288
+ You should see several additions:
289
+
290
+ ```diff
291
+ diff --git a/seeding_output b/seeding_output
292
+ index 1fc7ea7..7c4d768 100644
293
+ --- a/seeding_output
294
+ +++ b/seeding_output
295
+ @@ -16,6 +16,8 @@ Adding shipping services...
296
+ Adding assets...
297
+ Adding categories...
298
+ Adding products...
299
+ +Adding package products...
300
+ +Adding clothing products...
301
+ Adding auxiliary pages...
302
+ Adding browsing pages...
303
+ Adding discounts...
304
+ @@ -25,10 +27,17 @@ Adding customer service navigation...
305
+ Adding system content...
306
+ Adding admin users...
307
+ Adding customers...
308
+ +Adding blogs...
309
+ +Adding blog entries...
310
+ +Adding blog comments...
311
+ Adding orders...
312
+ Adding inquiries...
313
+ Adding help articles...
314
+ Adding analytics...
315
+ +Adding gift cards...
316
+ +Adding reviews...
317
+ +Adding store locations...
318
+ +Adding wish lists...
319
+
320
+ == Loading Elasticsearch data
321
+ Indexing storefront...
322
+ ```
323
+
324
+ Additionally, count the products again:
325
+
326
+ ```bash
327
+ $ bin/rails r 'puts Workarea::Catalog::Product.count'
328
+ 103
329
+ ```
330
+
331
+ If you compare the result to the same query above, you can see there are additional products in the database. These additional products include data to support the functionality of the clothing, package products, and gift cards plugins. To boot, the reviews plugin adds rating and review data to _all_ products. You can see some of these new products and additional data in the following view of the Storefront:
332
+
333
+ ![Seeds from plugins](images/seeds-from-plugins.png)
334
+
335
+ ## Summary
336
+
337
+ - Seeds are default data, which make your application usable for development and generally consistent with the other developers on your team
338
+ - Seeding, performed from the command line, is the (destructive) process of writing seeds to an environment
339
+ - Re-seeding should produce uniform, but not identical, data each time
340
+ - You should re-seed your development environment as needed to resolve data issues and after you or another developer extends seeds or changes installed plugins
341
+ - Plugins often include their own seeds to support the plugins' other extensions to the platform
342
+
343
+ ## Notes
344
+
345
+ [1] Of course, since seeds are extensible, you can write your own seeds to be idempotent if you so desire.
@@ -0,0 +1,756 @@
1
+ ---
2
+ title: Shipping
3
+ excerpt: A shipping (Workarea::Shipping) is an application document that represents shipping and pricing information for an order or a subset of an order.
4
+ ---
5
+
6
+ # Shipping
7
+
8
+ ## Shipping
9
+
10
+ A <dfn>shipping</dfn> (`Workarea::Shipping`) is an [application document](application-document.html) that represents shipping and pricing information for an order or a subset of an order.
11
+
12
+ ### Order
13
+
14
+ A shipping is associated with an order. An order may be associated with many shippings, but without customization, the Workarea checkout models and UI assume one shipping per order. The `order_id` field on a shipping identifies its associated order.
15
+
16
+ ```
17
+ # returns the first shipping for the given order id
18
+ Workarea::Shipping.find_by_order(order_id)
19
+
20
+ # returns all shippings with the given order id
21
+ Workarea::Shipping.where(order_id: order_id).to_a
22
+ ```
23
+
24
+ ### Address
25
+
26
+ A shipping embeds one shipping address. `Shipping#set_address` builds and saves the embedded address from the given attributes.
27
+
28
+ ```
29
+ shipping.set_address(
30
+ first_name: 'Bob',
31
+ last_name: 'Clams',
32
+ street: '22 S 3rd St',
33
+ city: 'Philadelphia',
34
+ region: 'PA',
35
+ postal_code: '19106',
36
+ country: 'US'
37
+ )
38
+ # => true
39
+
40
+ shipping.address.class
41
+ # => Workarea::Shipping::Address
42
+
43
+ shipping.address.first_name
44
+ # => "Bob"
45
+ ```
46
+
47
+ ### Service Selection
48
+
49
+ A shipping embeds one service selection. `Shipping#apply_shipping_service` builds the embedded service selection without persisting it. `Shipping#set_shipping_service` builds and saves the embedded service selection, using `apply_shipping_service` within its implementation.
50
+
51
+ `Shipping#apply_shipping_service` resets the price adjustments and shipping total on the shipping and then sets a _shipping_ price adjustment on the shipping. This price adjustment represents the base price of the service selection. See pricing example, below.
52
+
53
+ ### Pricing
54
+
55
+ A shipping embeds many price adjustments. While order items embed all _item_ and _order_ price adjustments, shippings embed all _shipping_ and _tax_ price adjustments. The price adjustments on a shipping may represent the following:
56
+
57
+ - The base price of the service selection embedded within the shipping
58
+ - Each discount on the service selection embedded within the shipping
59
+ - The tax price of the service selection embedded within the shipping
60
+ - The tax price of each order item associated with the shipping
61
+
62
+ `Shipping#base_price` returns the `amount` of the _shipping_ price adjustment on the shipping, if any. `Shipping#shipping_total` and `Shipping#tax_total` are `Money` values that default to zero when a shipping is initialized and are updated when the shipping is priced by the Workarea pricing model. See pricing example, below.
63
+
64
+ ### Pricing Example
65
+
66
+ ```
67
+ # create tax category
68
+ Workarea::Tax::Category.destroy_all
69
+ sales_tax_category = Workarea::Tax::Category.create!(
70
+ name: 'Sales Tax',
71
+ code: '001'
72
+ )
73
+
74
+ # create tax rate
75
+ sales_tax_category.rates.create!(
76
+ country: 'US',
77
+ region: 'PA',
78
+ percentage: 0.05
79
+ )
80
+
81
+ # create pricing sku
82
+ Workarea::Pricing::Sku.destroy_all
83
+ Workarea::Pricing::Sku.create!(
84
+ _id: 'small-shirt',
85
+ prices: [{ regular: 10 }],
86
+ tax_code: '001'
87
+ )
88
+
89
+ # create shipping service
90
+ Workarea::Shipping::Service.destroy_all
91
+ shipping_service = Workarea::Shipping::Service.create!(
92
+ name: 'Standard', rates: [{ price: 6 }], tax_code: '001'
93
+ )
94
+
95
+ # create shipping discount
96
+ Workarea::Pricing::Discount::Shipping.destroy_all
97
+ Workarea::Pricing::Discount::Shipping.create!(
98
+ name: "$5 #{shipping_service.name} Shipping",
99
+ shipping_service: shipping_service.name,
100
+ amount: 5
101
+ )
102
+
103
+ # create order with item
104
+ Workarea::Order.destroy_all
105
+ order = Workarea::Order.create!(
106
+ items: [{ product_id: 'shirt', sku: 'small-shirt', quantity: '1' }]
107
+ )
108
+
109
+ # create shipping
110
+ Workarea::Shipping.destroy_all
111
+ shipping = Workarea::Shipping.create!(order_id: order.id)
112
+
113
+ # set shipping address
114
+ shipping.set_address(
115
+ first_name: 'Bob',
116
+ last_name: 'Clams',
117
+ street: '22 S 3rd St',
118
+ city: 'Philadelphia',
119
+ region: 'PA',
120
+ postal_code: '19106',
121
+ country: 'US'
122
+ )
123
+
124
+ # set shipping service selection
125
+ Workarea::Pricing.perform(order)
126
+ shipping_option = shipping_service.to_option(order.subtotal_price)
127
+ shipping.set_shipping_service(shipping_option.to_h)
128
+
129
+ # price the order and shipping
130
+ Workarea::Pricing.perform(order, shipping)
131
+
132
+ shipping.price_adjustments.map(&:price)
133
+ # => ["shipping", "shipping", "tax", "tax"]
134
+
135
+ shipping.price_adjustments.map(&:description)
136
+ # => ["Standard", "$5 Standard Shipping", "Tax", "Tax"]
137
+
138
+ shipping.price_adjustments.map(&:calculator)
139
+ # => ["Workarea::Shipping", "Workarea::Pricing::Discount::Shipping", "Workarea::Pricing::TaxApplier", "Workarea::Pricing::TaxApplier"]
140
+
141
+ shipping.price_adjustments.map { |a| a.amount.to_s }
142
+ # => ["6.00", "-1.00", "0.50", "0.25"]
143
+
144
+ shipping.base_price.to_s
145
+ # => "6.00"
146
+
147
+ shipping.shipping_total.to_s
148
+ # => "5.00"
149
+
150
+ shipping.tax_total.to_s
151
+ # => "0.75"
152
+ ```
153
+
154
+ ### Options
155
+
156
+ `Shipping#find_method_options` returns shipping options that qualify for the shipping and the given packages. Each shipping option is created from a rate estimate provided by the configured carrier. Price adjustments are not set on these options, so the price of each reflects the base price only.
157
+
158
+ ```
159
+ # clear all shipping services
160
+ Workarea::Shipping::Service.destroy_all
161
+
162
+ # create 2 location agnostic shipping services
163
+ Workarea::Shipping::Service.create!(
164
+ name: 'Standard',
165
+ rates: [{ price: 5 }]
166
+ )
167
+ Workarea::Shipping::Service.create!(
168
+ name: 'Priority',
169
+ rates: [{ price: 10 }]
170
+ )
171
+
172
+ # create 2 location specific shipping services
173
+ Workarea::Shipping::Service.create!(
174
+ name: 'PA Standard',
175
+ rates: [{ price: 5 }],
176
+ country: 'US',
177
+ regions: ['PA']
178
+ )
179
+ Workarea::Shipping::Service.create!(
180
+ name: 'PA Priority',
181
+ rates: [{ price: 10 }],
182
+ country: 'US',
183
+ regions: ['PA']
184
+ )
185
+
186
+ # create order and shipping
187
+ order = Workarea::Order.create!
188
+ shipping = Workarea::Shipping.create!(
189
+ order_id: order.id,
190
+ address: {
191
+ first_name: 'Bob',
192
+ last_name: 'Clams',
193
+ street: '22 S 3rd St',
194
+ city: 'Philadelphia',
195
+ region: 'PA',
196
+ postal_code: '19106',
197
+ country: 'US'
198
+ }
199
+ )
200
+
201
+ # init packages
202
+ packages = Workarea::Packaging.new(order, shipping).packages
203
+
204
+ # list names of ALL shipping services
205
+ Workarea::Shipping::Service.all.to_a.map(&:name)
206
+ # => ["Standard", "Priority", "PA Standard", "PA Priority"]
207
+
208
+ # list names of QUALIFYING shipping options (each corresponding to a service)
209
+ shipping.find_method_options(packages).map(&:name)
210
+ # => ["PA Standard", "PA Priority"]
211
+ ```
212
+
213
+ ## Address
214
+
215
+ A <dfn>shipping address</dfn> (`Workarea::Shipping::Address`) is an embedded [application document](application-document.html) that represents the address to which its parent shipping is to be shipped.
216
+
217
+ ### Fields
218
+
219
+ A shipping address has the following fields (required fields indicated).
220
+
221
+ - `first_name` (required)
222
+ - `last_name` (required)
223
+ - `company`
224
+ - `street` (required)
225
+ - `street_2`
226
+ - `city` (required)
227
+ - `region` (required if country has regions)
228
+ - `postal_code` (required if country has postal codes)
229
+ - `country` (required)
230
+ - `phone_number`
231
+ - `phone_extension`
232
+
233
+ A shipping address also applies the following validations and transformations.
234
+
235
+ - All fields have a max length of 500 characters
236
+ - `street` and `street_2` cannot be a PO Box, as defined by the pattern in `Workarea.config.po_box_regex`
237
+ - The `phone_number` setter strips non-digits
238
+
239
+ ### ActiveShipping
240
+
241
+ `Shipping::Address#to_active_shipping` initializes and returns an ActiveShipping location representing the address.
242
+
243
+ ```
244
+ shipping = Workarea::Shipping.create!(
245
+ address: {
246
+ first_name: 'Bob',
247
+ last_name: 'Clams',
248
+ street: '22 S 3rd St',
249
+ city: 'Philadelphia',
250
+ region: 'PA',
251
+ postal_code: '19106',
252
+ country: 'US'
253
+ }
254
+ )
255
+
256
+ # address attributes
257
+ shipping.address.as_document
258
+ # => {
259
+ # "_id" => BSON::ObjectId('58d42851eefbfefd52cc4b6b'),
260
+ # "_type" => "Workarea::Shipping::Address",
261
+ # "first_name" => "Bob",
262
+ # "last_name" => "Clams",
263
+ # "street" => "22 S 3rd St",
264
+ # "city" => "Philadelphia",
265
+ # "region" => "PA",
266
+ # "postal_code" => "19106",
267
+ # "country" => "US"
268
+ # }
269
+
270
+ # location attributes
271
+ shipping.address.to_active_shipping.to_hash
272
+ # => {
273
+ # :country => "US",
274
+ # :postal_code => "19106",
275
+ # :province => "PA",
276
+ # :city => "Philadelphia",
277
+ # :name => nil,
278
+ # :address1 => nil,
279
+ # :address2 => nil,
280
+ # :address3 => nil,
281
+ # :phone => nil,
282
+ # :fax => nil,
283
+ # :address_type => nil,
284
+ # :company_name => nil
285
+ # }
286
+ ```
287
+
288
+ ## Service Selection
289
+
290
+ A <dfn>shipping service selection</dfn> (`Workarea::Shipping::ServiceSelection`) is an embedded [application document](application-document.html) that represents the shipping service for its parent shipping.
291
+
292
+ A service selection's `name`, `carrier`, and `service_code` should identify a shipping service within the retailer's fulfillment system. The `tax_code` field identifies the applicable tax category within the Workarea system.
293
+
294
+ ## Shipping Option
295
+
296
+ A <dfn>shipping option</dfn> (`Workarea::ShippingOption`) is an object representing a _qualifying_ shipping service for an order or shipping (if multiple shippings). While a rate estimate represents a shipping service from the carrier's perspective, a shipping option represents a shipping service from the perspective of the Workarea system, including `tax_code` and `price_adjustments`.
297
+
298
+ ```
299
+ shipping_option.name
300
+ # => "Media Mail"
301
+
302
+ shipping_option.carrier
303
+ # => "USPS"
304
+
305
+ shipping_option.service_code
306
+ # => "Media Mail Parcel"
307
+
308
+ shipping_option.tax_code
309
+ # => "001"
310
+ ```
311
+
312
+ ### Pricing
313
+
314
+ Within checkout, shipping options are presented with an adjusted price (the absolute sum of all _shipping_ price adjustments). After a customer selects a shipping option, the selected shipping option is used to persist a service selection on the shipping. The service selection includes the `tax_code`, which is used to calculate a `tax` price adjustment on the shipping.
315
+
316
+ A shipping option may be initialized with a price (stored in `@price`) and may have price adjustments (stored in `@price_adjustments`). The methods `price` and `base_price` return the adjusted and non-adjusted price, respectively. When `@price_adjustments` is empty, both methods return the same value.
317
+
318
+ ```
319
+ shipping_option.price_adjustments.map{ |a| a.amount.to_s }
320
+ # => ["-1.00"]
321
+
322
+ shipping_option.base_price.to_s
323
+ # => "6.00"
324
+
325
+ shipping_option.price.to_s
326
+ # => "5.00"
327
+ ```
328
+
329
+ ### Initializing
330
+
331
+ A shipping option can be initialized from an rate estimate or a shipping service. Additionally, `Shipping#find_method_options` and `Checkout::ShippingOptions#available` return collections of shipping options.
332
+
333
+ #### From Rate Estimate
334
+
335
+ When initializing from a rate estimate, `price` is typecast to `Money`. Additionally, `Shipping::Service.find_tax_code` finds the value for `tax_code` using the `carrier` and `service_name`. When an application is configured to use a shipping carrier other than `ActiveShipping::Workarea`, shipping service models are likely not used for rate estimates. However, they will still be used to look up tax codes. Any application concerned with shipping service tax codes must therefore persist shipping services.
336
+
337
+ ```
338
+ # rate estimate attributes
339
+ JSON.parse(rate_estimate.to_json)
340
+ => {
341
+ # "origin" => {} # truncated
342
+ # "destination" => {} # truncated
343
+ # "carrier" => "USPS",
344
+ # "service_name" => "Media Mail",
345
+ # "service_code" => "Media Mail Parcel",
346
+ # "description" => nil,
347
+ # "estimate_reference" => nil,
348
+ # "pickup_time" => nil,
349
+ # "expires_at" => nil,
350
+ # "package_rates" => [],
351
+ # "total_price" => 600,
352
+ # "negotiated_rate" => nil,
353
+ # "compare_price" => nil,
354
+ # "phone_required" => false,
355
+ # "currency" => "USD",
356
+ # "delivery_range" => [],
357
+ # "shipping_date" => nil,
358
+ # "delivery_date" => nil,
359
+ # "insurance_price" => nil,
360
+ # "delivery_category" => nil,
361
+ # "shipment_options" => [],
362
+ # "charge_items" => []
363
+ # }
364
+
365
+ # shipping option attributes
366
+ Workarea::ShippingOption.from_rate_estimate(rate_estimate).to_h
367
+ # => {
368
+ # :carrier => "USPS",
369
+ # :name => "Media Mail",
370
+ # :service_code => "Media Mail Parcel",
371
+ # :price => #<Money fractional:600 currency:USD>,
372
+ # :base_price => #<Money fractional:600 currency:USD>,
373
+ # :tax_code => "001"
374
+ # }
375
+ ```
376
+
377
+ #### From Shipping Service
378
+
379
+ `ShippingService#to_option(subtotal)` initializes a shipping option from a shipping service, using the provided subtotal to look up the qualifying rate. The shipping service `name` is flattened to a string (from localization hash) and `price` is added.
380
+
381
+ ```
382
+ # shipping service attributes
383
+ JSON.parse(shipping_service.to_json)
384
+ # => {
385
+ # "_id" => "58d43b5aeefbfefd52cc4bc0",
386
+ # "carrier" => "USPS",
387
+ # "country" => nil,
388
+ # "created_at" => "2017-03-23T21:17:14.731Z",
389
+ # "name" => "Media Mail",
390
+ # "rates" => [{
391
+ # "_id" => "58d43b5aeefbfefd52cc4bc1",
392
+ # "created_at" => nil,
393
+ # "price" => {"cents" => 500.0, "currency_iso" => "USD"},
394
+ # "tier_max" => nil,
395
+ # "tier_min" => nil,
396
+ # "updated_at" => nil
397
+ # }],
398
+ # "regions" => nil,
399
+ # "service_code" => "Media Mail Parcel",
400
+ # "subtotal_max" => nil,
401
+ # "subtotal_min" => nil,
402
+ # "tax_code" => "001",
403
+ # "updated_at" => "2017-03-23T21:17:14.731Z"
404
+ # }
405
+
406
+ # shipping option attributes
407
+ shipping_service.to_option(25).to_h
408
+ # => {
409
+ # :carrier => "USPS",
410
+ # :name => "Media Mail",
411
+ # :service_code => "Media Mail Parcel",
412
+ # :price => #<Money fractional:500 currency:USD>,
413
+ # :base_price => #<Money fractional:500 currency:USD>,
414
+ # :tax_code => "001"
415
+ # }
416
+ ```
417
+
418
+ #### From Shipping & Packages
419
+
420
+ `Shipping#find_method_options` requests rate estimates from the configured shipping carrier and maps each to a shipping option, returning a collection of shipping options (see options, above). This method uses `ShippingOption.from_rate_estimate` within its implementation (see from rate estimate, above).
421
+
422
+ #### From Order & Shipping
423
+
424
+ The Workarea checkout service uses `Workarea::Checkout::ShippingOptions#available` to return the collection of shipping options that qualify for the checkout, using information from the checkout's order and shipping. Since this API has access to an order and a shipping, the shipping options it returns have price adjustments representing any discounts on the shipping service selection. **This is the only API that returns shipping options with price adjustments.**
425
+
426
+ ```
427
+ # create 2 shipping services
428
+ Workarea::Shipping::Service.destroy_all
429
+ standard_shipping_service = Workarea::Shipping::Service.create!(
430
+ name: 'Standard',
431
+ rates: [{ price: 6 }]
432
+ )
433
+ free_shipping_service = Workarea::Shipping::Service.create!(
434
+ name: 'FREE',
435
+ rates: [{ price: 0 }],
436
+ subtotal_min: 50
437
+ )
438
+
439
+ # create shipping discount
440
+ Workarea::Pricing::Discount::Shipping.destroy_all
441
+ Workarea::Pricing::Discount::Shipping.create!(
442
+ name: "$5 #{shipping_service.name} Shipping",
443
+ shipping_service: shipping_service.name,
444
+ amount: 5
445
+ )
446
+
447
+ # create pricing sku
448
+ Workarea::Pricing::Sku.destroy_all
449
+ Workarea::Pricing::Sku.create!(
450
+ _id: 'small-shirt',
451
+ prices: [{ regular: 10 }]
452
+ )
453
+
454
+ # create order with item
455
+ Workarea::Order.destroy_all
456
+ order = Workarea::Order.create!(
457
+ items: [{ product_id: 'shirt', sku: 'small-shirt', quantity: '1' }]
458
+ )
459
+
460
+ # create shipping with address
461
+ Workarea::Shipping.destroy_all
462
+ shipping = Workarea::Shipping.create!(
463
+ order_id: order.id,
464
+ address: {
465
+ first_name: 'Bob',
466
+ last_name: 'Clams',
467
+ street: '22 S 3rd St',
468
+ city: 'Philadelphia',
469
+ region: 'PA',
470
+ postal_code: '19106',
471
+ country: 'US'
472
+ }
473
+ )
474
+
475
+ # price the order and shipping
476
+ Workarea::Pricing.perform(order, shipping)
477
+
478
+ shipping_options = Workarea::Checkout::ShippingOptions.new(order, shipping).available
479
+
480
+ # ALL shipping services
481
+ Workarea::Shipping::Service.count
482
+ # => 2
483
+
484
+ # QUALIFYING shipping options
485
+ shipping_options.count
486
+ # => 1
487
+
488
+ shipping_option = shipping_options.first
489
+
490
+ shipping_option.name
491
+ # => "Standard"
492
+
493
+ # price, including discount(s)
494
+ shipping_option.price.to_s
495
+ # => "5.00"
496
+
497
+ # find corresponding shipping service
498
+ shipping_service = Workarea::Shipping::Service.find_by(name: shipping_option.name)
499
+
500
+ # base price of shipping service (excludes discounts)
501
+ shipping_service.find_rate(order.subtotal_price).price.to_s
502
+ # => "6.00"
503
+ ```
504
+
505
+ ### Serializing
506
+
507
+ `ShippingOption#to_h` serializes a shipping option into attributes appropriate for the initialization of a shipping service selection. This method is used to persist a shipping option as a shipping service selection via `Shipping#apply_shipping_service`.
508
+
509
+ ```
510
+ shipping_option.to_h
511
+ # => {
512
+ # :carrier => "USPS",
513
+ # :name => "Media Mail",
514
+ # :service_code => "Media Mail Parcel",
515
+ # :price => #<Money fractional:600 currency:USD>,
516
+ # :base_price => #<Money fractional:600 currency:USD>,
517
+ # :tax_code => "001"
518
+ # }
519
+ ```
520
+
521
+ ## ActiveShipping
522
+
523
+ Workarea uses [ActiveShipping (v1.8)](http://www.rubydoc.info/gems/active_shipping/1.8.6), a Ruby API that abstracts the web services of various shipping carriers.
524
+
525
+ ### Carrier
526
+
527
+ A Workarea application is configured with an ActiveShipping carrier (`ActiveShipping::Carrier`, [docs](http://www.rubydoc.info/gems/active_shipping/1.8.6/ActiveShipping/Carrier)) from which it will request shipping rates using `ActiveShipping::Carrier#find_rates` ([docs](http://www.rubydoc.info/gems/active_shipping/1.8.6/ActiveShipping/Carrier#find_rates-instance_method)). `find_rates` has the following signature.
528
+
529
+ ```
530
+ ActiveShipping::Carrier#find_rates(origin, destination, packages, options = {}) ⇒ ActiveShipping::RateResponse
531
+ ```
532
+
533
+ A rate request therefore requires `origin` and `destination`, which are locations, and a collection of packages. For most carriers, the rate request happens over HTTP. The request returns a response, which, if successful, includes a collection of rate estimates.
534
+
535
+ ### Workarea Shipping Carrier
536
+
537
+ `Workarea.config.gateways.shipping` holds an instance of the currently configured carrier. The default shipping carrier is `ActiveShipping::Workarea`, which implements `find_rates` as follows.
538
+
539
+ ```
540
+ Workarea::Shipping::RateLookup.new(origin, destination, packages, options).response
541
+ ```
542
+
543
+ This implementation does not require an HTTP request, and the response from `Shipping::RateLookup#response` is always successful. The returned collection of rate estimates is initialized from the set of qualifying shipping services persisted in MongoDB. To determine qualifying shipping services, services are queried by country and region (using the address on the shipping) and by price (using the order subtotal). The services within the intersection of these queries are used to initialize the collection of rate estimates for the response.
544
+
545
+ ### Location
546
+
547
+ An ActiveShipping location (`ActiveShipping::Location`, [docs](http://www.rubydoc.info/gems/active_shipping/1.8.6/ActiveShipping/Location)) represents either the origin or destination location to use during fulfillment of an order.
548
+
549
+ The destination location is created from the shipping address, while the origin is created from the attributes held in `Workarea.config.shipping_origin`.
550
+
551
+ ```
552
+ Workarea.config.shipping_origin
553
+ # => {:country=>"US", :state=>"PA", :city=>"Philadelphia", :zip=>"19106"}
554
+ ```
555
+
556
+ ### Package
557
+
558
+ An ActiveShipping package (`ActiveShipping::Package`, [docs](http://www.rubydoc.info/gems/active_shipping/1.8.6/ActiveShipping/Package)) represents a physical package to be delivered as part of order fulfillment. See packaging, below.
559
+
560
+ ### Response
561
+
562
+ An ActiveShipping response (`ActiveShipping::Response`, [docs](http://www.rubydoc.info/gems/active_shipping/1.8.6/ActiveShipping/Response)) represents the response from a carrier for rate estimates. With the exception of the Workarea shipping carrier, the request and response typically occurs over HTTP. The response will indicate success or failure and provide rate estimates.
563
+
564
+ ### Rate Estimate
565
+
566
+ An ActiveShipping rate estimate (`ActiveShipping::RateEstimate`, [docs](http://www.rubydoc.info/gems/active_shipping/1.8.6/ActiveShipping/RateEstimate)) represents a qualified shipping service for the customer to select. During checkout, rate estimates are used to initialize shipping options, which unlike rate estimates, can present adjusted pricing.
567
+
568
+ ## Sku
569
+
570
+ A <dfn>shipping sku</dfn> (`Workarea::Shipping::Sku`) is an [application document](application-document.html) that represents the shipping attributes for a catalog variant (`Workarea::Catalog::Variant`). The `_id` of a shipping sku matches the `sku` of the variant with which it is associated.
571
+
572
+ ```
573
+ variant.sku == shipping_sku.id.to_s
574
+ # => true
575
+ ```
576
+
577
+ Without customization, a shipping sku has `weight` and `dimensions` fields. By default, weight is measured in ounces and dimensions are measured in inches. An application can use metric units by changing the ActiveShipping options held in `Workarea.config.shipping_options`.
578
+
579
+ ```
580
+ Workarea.config.shipping_options
581
+ # => { :units => :imperial }
582
+ ```
583
+
584
+ The Workarea packaging service finds the shipping sku for each order item within an order to construct one or many packages for the order (or its associated shipping, if multiple shippings).
585
+
586
+ ## Packaging
587
+
588
+ The Workarea <dfn>packaging service</dfn> (Workarea::Packaging) initializes a collection of packages for a given order and shipping. Packages are needed to request rate estimates from an ActiveShipping carrier.
589
+
590
+ ### Default Implementation
591
+
592
+ Without customization, the Workarea checkout service assumes one shipping per order. By default, the packaging service therefore ignores the provided shipping and always returns a collection with a single package representing the entire order.
593
+
594
+ The weight and dimensions of the package are derived from the combined weight and dimensions of all items to be included in the package, as stored on the shipping sku that corresponds to each order item. If a shipping sku is not persisted for an item in the order, a new shipping sku (with default values) is used to represent that item in the package.
595
+
596
+ If the dimensions of any item in the package are unknown, the value held in `Workarea.config.shipping_dimensions` is used as the dimensions of the package. For this reason, this value should represent the average or standard box size used to fulfill orders.
597
+
598
+ ```
599
+ Workarea.config.shipping_dimensions
600
+ # => [1, 1, 1]
601
+ ```
602
+
603
+ The following example demonstrates the process of initializing packages from an order and shipping.
604
+
605
+ ```
606
+ Workarea::Catalog::Product.destroy_all
607
+ product = Workarea::Catalog::Product.create!(
608
+ name: 'shirt',
609
+ variants: [{ sku: 'large-shirt' }]
610
+ )
611
+ sku = product.variants.first.sku
612
+ Workarea::Shipping::Sku.destroy_all
613
+ Workarea::Shipping::Sku.create!(
614
+ _id: sku,
615
+ weight: 5.0,
616
+ dimensions: [11, 9, 2]
617
+ )
618
+ Workarea::Order.destroy_all
619
+ order = Workarea::Order.create!(
620
+ items: [{
621
+ product_id: product.id,
622
+ sku: sku,
623
+ quantity: '1'
624
+ }]
625
+ )
626
+ Workarea::Shipping.destroy_all
627
+ shipping = Workarea::Shipping.create!(order_id: order.id)
628
+ package = Workarea::Packaging.new(order, shipping).packages.first
629
+
630
+ package.ounces
631
+ # => 5.0
632
+
633
+ package.inches
634
+ # => [2, 9, 11]
635
+ ```
636
+
637
+ ### Extension
638
+
639
+ Although it receives a collection of packages, the default shipping carrier ignores them when constructing its list of qualifying rate estimates. However, this is unlikely to be true for other carriers.
640
+
641
+ The default packaging logic may be too naive for some applications, but the complexities required to handle all possibilities is beyond the scope of the Workarea platform. Applications using a shipping carrier that relies on packaging may need to decorate the packaging service to implement the logic preferred by the retailer.
642
+
643
+ ## Service
644
+
645
+ A <dfn>shipping service</dfn> (`Workarea::Shipping::Service`) is an [application document](application-document.html) that represents a shipping service by which an order can be fulfilled. A shipping service may represent a specific shipping product, such as _FedEx Ground_, or may represent a more abstract shipping service, such as _Standard Shipping_ or _Free Shipping_.
646
+
647
+ The Workarea shipping carrier uses the persisted shipping services to construct a collection of rate estimates, which are then used to initialize shipping options to present to the customer during checkout.
648
+
649
+ ### Fields
650
+
651
+ A shipping service must have a `name`, and may also have values for `carrier`, `service_code`, and `tax_code`.
652
+
653
+ ```
654
+ shipping_service.name
655
+ # => "Media Mail"
656
+
657
+ shipping_service.carrier
658
+ # => "USPS"
659
+
660
+ shipping_service.service_code
661
+ # => "Media Mail Parcel"
662
+
663
+ shipping_service.tax_code
664
+ # => "001"
665
+ ```
666
+
667
+ ### Rates
668
+
669
+ A service embeds many rates representing the base prices of the service. `Shipping::Service#find_rate` finds the lowest qualifying rate for the given amount (typically the order subtotal).
670
+
671
+ ```
672
+ standard_shipping_service = Workarea::Shipping::Service.create!(
673
+ name: 'Standard',
674
+ rates: [
675
+ { price: 5, tier_max: 49.99 },
676
+ { price: 10, tier_max: 99.99 },
677
+ { price: 15, tier_min: 100 }
678
+ ]
679
+ )
680
+
681
+ standard_shipping_service.find_rate((49.99).to_m).price.to_s
682
+ # => "5.00"
683
+
684
+ standard_shipping_service.find_rate((50.00).to_m).price.to_s
685
+ # => "10.00"
686
+
687
+ standard_shipping_service.find_rate((99.99).to_m).price.to_s
688
+ # => "10.00"
689
+
690
+ standard_shipping_service.find_rate((100.00).to_m).price.to_s
691
+ # => "15.00"
692
+
693
+ standard_shipping_service.find_rate((500.00).to_m).price.to_s
694
+ # => "15.00"
695
+ ```
696
+
697
+ ### Querying
698
+
699
+ `Shipping::Service.for_location` queries services by country and region, while `Shipping::Service.by_price` queries services by price.
700
+
701
+ ### By Country & Region
702
+
703
+ ```
704
+ Workarea::Shipping::Service.destroy_all
705
+ Workarea::Shipping::Service.create!(name: 'Standard', rates: [{ price: 5 }])
706
+ Workarea::Shipping::Service.create!(name: 'Priority', rates: [{ price: 10 }])
707
+ Workarea::Shipping::Service.create!(name: 'Express', rates: [{ price: 20 }])
708
+
709
+ Workarea::Shipping::Service.for_location('US', 'PA').map(&:name)
710
+ # => ["Standard", "Priority", "Express"]
711
+
712
+ standard_shipping_service = Workarea::Shipping::Service.find_by(name: 'Standard')
713
+ standard_shipping_service.update_attributes!(country: 'US', regions: ['PA'])
714
+
715
+ Workarea::Shipping::Service.for_location('US', 'PA').map(&:name)
716
+ # => ["Standard"]
717
+ ```
718
+
719
+ ### Services by Price
720
+
721
+ ```
722
+ Workarea::Shipping::Service.destroy_all
723
+ Workarea::Shipping::Service.create!(name: 'Standard', rates: [{ price: 10 }])
724
+ Workarea::Shipping::Service.create!(name: 'FREE', rates: [{ price: 0 }], subtotal_min: 50)
725
+
726
+ Workarea::Shipping::Service.by_price((49.99).to_m).map(&:name)
727
+ # => ["Standard"]
728
+
729
+ Workarea::Shipping::Service.by_price((50.00).to_m).map(&:name)
730
+ # => ["Standard", "FREE"]
731
+
732
+ standard_shipping_service = Workarea::Shipping::Service.find_by(name: 'Standard')
733
+ standard_shipping_service.update_attributes!(subtotal_max: 49.99)
734
+ Workarea::Shipping::Service.by_price((49.99).to_m).map(&:name)
735
+ # => ["Standard"]
736
+
737
+ Workarea::Shipping::Service.by_price((50.00).to_m).map(&:name)
738
+ # => ["FREE"]
739
+ ```
740
+
741
+ ### Rate
742
+
743
+ A <dfn>shipping rate</dfn> (Workarea::Shipping::Rate) is an embedded [application document](application-document.html) that represents a price for its parent shipping service as well as any data that may be used to qualify the rate.
744
+
745
+ By default, a rate is qualified by comparing its `tier_min` and `tier_max` to a given amount.
746
+
747
+ A rate has a required `price` field that represents a base price for the parent service.
748
+
749
+ ```
750
+ shipping_service.rates.first.price.to_s
751
+ # => "5.00"
752
+
753
+ shipping_service.rates.first.tier_max.to_s
754
+ # => "49.99"
755
+ ```
756
+