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,148 @@
1
+ ---
2
+ title: Progressive Web Application (PWA) Support
3
+ excerpt: Learn how to modify and extend the out-of-the-box solution for asset precaching provided by the platform
4
+ ---
5
+
6
+ # Progressive Web Application Support
7
+
8
+ The Workarea Commerce Platform ships with the foundational implementation of a [Progressive Web Application](https://developer.mozilla.org/en-US/docs/Web/Apps/Progressive) (PWA) which prompts a user to install the application directly to their smartphone home screen. This functionality is provided by [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API).
9
+
10
+ The following sections cover an overview of the files involved and some more information about various caching techniques you can employ to custom fit the PWA to your client's specific needs.
11
+
12
+ ## Overview
13
+
14
+ Support for Service Workers in the platform is provided by the [serviceworker-rails gem](https://github.com/rossta/serviceworker-rails). The main benefit this gem provides is its middleware, which allows us to serve each Service Worker from the website's root context and more granularly control response headers. It should be noted that the gem also provides a generator which is disabled by the platform, as its functionality is not useful to the host application. Overriding of each file can be done using the [workarea:override generator](articles/overriding.html).
15
+
16
+ ### Configuration
17
+
18
+ Out of the box the platform configures the `serviceworker-rails` gem to provide a route to the main `pwa_cache.js` Service Worker, then adds that routed file for precompilation:
19
+
20
+ ```rb
21
+ # core/config/initializers/21_serviceworkers.rb
22
+
23
+ ...
24
+
25
+ # Configure serviceworker-rails
26
+ app.config.serviceworker.routes.draw do
27
+ match '/pwa_cache.js' => 'workarea/storefront/serviceworkers/pwa_precache.js'
28
+ end
29
+
30
+ # Precompile the required assets
31
+ app.config.assets.precompile += %w(pwa_cache.js)
32
+ ```
33
+
34
+ Each Service Worker written for the website will need to be handled similarly. You can [adjust this configuration in an initializer](articles/configuration.html) within the host application.
35
+
36
+ ### Registering a Service Worker
37
+
38
+ Though Service Workers are not served from within the [JavaScript manifest](/articles/add-javascript-through-a-manifest.html) they are registered through files that are. These [registration scripts](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Registering_your_worker) live at the top of the manifest in the following section:
39
+
40
+ ```rb
41
+ # Service Worker Registrations
42
+ %w(
43
+ workarea/storefront/serviceworkers/register_pwa_cache
44
+ ).each do |asset|
45
+ require_asset asset
46
+ end
47
+
48
+ # Plugin Service Worker Registrations
49
+ append_javascripts('storefront.serviceworker_registrations')
50
+
51
+ ...
52
+ ```
53
+
54
+ The contents of the `register_pwa_cache.js` file will use the configured route during registration:
55
+
56
+ ```js
57
+ // app/assets/javascripts/workarea/storefront/serviceworkers/register_pwa_cache.js
58
+
59
+ 'use strict';
60
+
61
+ if ('serviceWorker' in navigator) {
62
+ navigator.serviceWorker.register('/pwa_cache.js', { scope: './' });
63
+ }
64
+ ```
65
+
66
+ ### Asset Pre-Caching
67
+
68
+ The [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) provides the lifecycle for the `pwa_cache.js` script, which is only responsible for the initial precaching of assets (after installing the PWA) and the display of an offline error page (when a network request is made) if the device disconnects from the network during a request. The offline error page messaging is provided by System Content and is administrable within the Admin.
69
+
70
+ ```js
71
+ // app/assets/javascripts/workarea/storefront/serviceworkers/pwa_cache.js
72
+
73
+ 'use strict';
74
+
75
+ /**
76
+ * On PWA installation, pre-cache the assets & offline page
77
+ */
78
+ self.addEventListener('install', function (event) {
79
+ event.waitUntil(
80
+ caches.open('pwa_cache').then(function (cache) {
81
+ return cache.addAll([
82
+ '<%= image_path("workarea/storefront/logo.png") %>',
83
+ '<%= stylesheet_path("workarea/storefront/application.css") %>',
84
+ '<%= javascript_path("workarea/storefront/head.js") %>',
85
+ '<%= javascript_path("workarea/storefront/application.js") %>',
86
+ '/offline'
87
+ ]);
88
+ })
89
+ );
90
+ });
91
+
92
+ /**
93
+ * On PWA fetch, serve the offline system content page if the connection fails
94
+ */
95
+ self.addEventListener('fetch', function (event) {
96
+ if (event.request.mode !== 'navigate') { return; }
97
+ if (event.request.method !== 'GET') { return; }
98
+ if ( ! event.request.headers.get('accept').includes('text/html')) { return; }
99
+
100
+ event.respondWith(
101
+ fetch(event.request).catch(function () {
102
+ return caches.match('/offline');
103
+ })
104
+ );
105
+ });
106
+ ```
107
+
108
+ During the `install` event, the cache is filled with a list of assets and an offline error page that will be displayed if and when the device loses connectivity while browsing. During the `fetch` event, we test the request to make sure it's a valid page request, and display said offline error page as needed.
109
+
110
+ You may be concerned about serving stale files from the cache at this point. Service Workers that have been installed to the device are regularly checked against the version hosted on the server. If they differ, they are reinstalled in the background. For each cached file whose path is provided by one of Rails' Asset Helpers (`image_path, `stylesheet_path`, etc) Sprockets will append a fingerprint to the filename, which will trigger the Service Worker to reinstall and update the cache automatically within a 24 hour window. This will also trigger an update for the offline error page if any changes have been made.
111
+
112
+ ## Adding Assets to the Cache
113
+
114
+ All types of assets can be added to the cache. We have chosen to add the site's logo, the Stylesheet and JavaScript manifests, and the offline error page as a sensible foundation. Caching font files, icons and other images in addition to what's already provided may be in your best interest.
115
+
116
+ If you need to add files to the cache, [override](/articles/overriding.html) the `pwa_cache.js` file and add any assets you'd like to store on the device in advance.
117
+
118
+ Adding a font might look something like this:
119
+
120
+ ```js
121
+ ...
122
+
123
+ return cache.addAll([
124
+
125
+ '<%= font_path("workarea/storefront/roboto.woff") %>',
126
+ '<%= font_path("workarea/storefront/roboto.woff2") %>',
127
+
128
+ '<%= image_path("workarea/storefront/logo.png") %>',
129
+ '<%= stylesheet_path("workarea/storefront/application.css") %>',
130
+ '<%= javascript_path("workarea/storefront/head.js") %>',
131
+ '<%= javascript_path("workarea/storefront/application.js") %>',
132
+ '/offline'
133
+ ]);
134
+
135
+ ...
136
+ ```
137
+
138
+ ## Employing Additional Caching Techniques
139
+
140
+ The platform takes a rather hands-off approach to prescribing what will be cached by the PWA. As you can see from the examples above no pages are even being cached by default. This is an intentional choice made to allow more flexibility in the host application.
141
+
142
+ There are many caching techniques out there, but here are some resources to help you achive a caching plan that's right for your application:
143
+
144
+ * [Google Developer's Offline Cookbook](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/)
145
+ * [Google Developer's Service Worker Lifecycle](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle)
146
+ * [Google Workbox](https://developers.google.com/web/tools/workbox/)
147
+ * [Progressive Web Applications on MDN](https://developer.mozilla.org/en-US/docs/Web/Apps/Progressive)
148
+ * [Service Workers Cookbook](https://serviceworke.rs/)
@@ -0,0 +1,33 @@
1
+ ---
2
+ title: Rails Asset Manifests
3
+ excerpt: Rails ships with a feature known as the asset pipeline, which is covered in detail in this excellent Rails guide. If you're new to Ruby on Rails, that guide and the other Rails guides are your friends.
4
+ ---
5
+
6
+ # Rails Asset Manifests
7
+
8
+ Rails ships with a feature known as the asset pipeline, which is covered in detail in [this excellent Rails guide](http://guides.rubyonrails.org/asset_pipeline.html). If you're new to Ruby on Rails, that guide and the other Rails guides are your friends.
9
+
10
+ The asset pipeline is used to bundle together assets and optimize them in production environments.
11
+
12
+ A manifest typically includes special comments, called directives, to add JavaScript files to a bundle. The contents of a **typical Rails manifest** looks something like this:
13
+
14
+ your\_app/app/assets/javascripts/application.js:
15
+
16
+ ```
17
+ //= require jquery
18
+ //= require lodash
19
+ //= require some_js_file
20
+ //= require some_other_js_file
21
+ //= require ...
22
+ //= require ...
23
+ //= require ...
24
+ //...
25
+ ```
26
+
27
+ You add a manifest like this to your app using [rails asset view helpers](rails-asset-view-helpers.html) provided by Rails. In a development environment, each file in the manfest is included as a separate `script` element. In production, the assets are concatenated into a single file (named after the manifest) and minified.
28
+
29
+ ## Workarea Asset Manifests
30
+
31
+ Workarea asset manifests leverage the Rails asset pipeline, but **Workarea's manifest files look a bit different**. See [Add JavaScript through a Manifest](add-javascript-through-a-manifest.html) for a detailed look at the Workarea manifest files.
32
+
33
+
@@ -0,0 +1,25 @@
1
+ ---
2
+ title: Rails Asset View Helpers
3
+ excerpt: Each Rails view that ships with Workarea uses view helpers provided by Rails to link to assets and construct asset tags, such as script tags that include JavaScript on the page.
4
+ ---
5
+
6
+ # Rails Asset View Helpers
7
+
8
+ Each Rails view that ships with Workarea uses view helpers provided by Rails to [link to assets](http://api.rubyonrails.org/classes/ActionView/Helpers/AssetUrlHelper.html) and [construct asset tags](http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html), such as `script` tags that include JavaScript on the page.
9
+
10
+ The head and application script tags mentioned in [the JavaScript overview](javascript-overview.html) are constructed in each Workarea layout by calling `javascript_include_tag`. Out of the box, the method is called twice in each layout, once in the document head and once in the body, as shown in the example below.
11
+
12
+ workarea-storefront/app/views/layouts/workarea/storefront/application.html.haml:
13
+
14
+ ```
15
+ %head
16
+ /...
17
+ = javascript_include_tag 'workarea/storefront/head'
18
+ /...
19
+ %body
20
+ /...
21
+ = javascript_include_tag 'workarea/storefront/application'
22
+ /...
23
+ ```
24
+
25
+
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: Reading Data
3
+ excerpt: A diagram to illustrate how data is read from the various data stores and is organized for display to the end-users.
4
+ ---
5
+
6
+ # Reading Data
7
+
8
+ The following diagram illustrates how data is read from the various data stores and is organized for display to the end-users.
9
+
10
+ ![Images Diagram](images/reading-data.svg)
@@ -0,0 +1,37 @@
1
+ ---
2
+ title: Releasable
3
+ excerpt: A releasable is an application document that includes the Workarea::Releasable module, and thereby has a boolean active field and many changesets (Workarea::Release::Changeset).
4
+ ---
5
+
6
+ # Releasable
7
+
8
+ A <dfn>releasable</dfn> is an [application document](application-document.html) that includes the `Workarea::Releasable` module, and thereby has a boolean `active` field and many changesets (`Workarea::Release::Changeset`).
9
+
10
+ ```
11
+ # create 'Catalog Update' release
12
+ release = Workarea::Release.create!(name: 'Catalog Update')
13
+
14
+ # create 'Shirt' inactive product (a releasable)
15
+ product = Workarea::Catalog::Product.create!(name: 'Shirt', activate_with: release.id)
16
+
17
+ # product is releasable
18
+ product.releasable?
19
+ # => true
20
+
21
+ # product is currently inactive
22
+ product.active?
23
+ # => false
24
+
25
+ # product has 1 changeset
26
+ product.changesets.count
27
+ # => 1
28
+
29
+ # changeset indicates product will become active when release publishes
30
+ product.changesets.first.changeset
31
+ => {"active"=>true}
32
+
33
+ # corresponding release is 'Catalog Update'
34
+ product.changesets.first.release.name
35
+ # => "Catalog Update"
36
+ ```
37
+
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: Report a Bug
3
+ excerpt: Learn how to report issues you experience on the Workarea Commerce Platform.
4
+ created_at: 2019/06/05
5
+ ---
6
+
7
+ # Report a Bug
8
+
9
+ Throughout your career as a Workarea developer, you may encounter bugs within the platform. Since Workarea's core is open-source, this means you are also given the means to contribute your own fixes to the platform and make every other developer's lives a bit easier (including your own). But sometimes you don't know how to best proceed with a given issue, or simply don't have time since you're busy launching a new site. If that's the case, you should **report the bug as an issue** to the Workarea project on GitHub.
10
+
11
+ ## Reporting a New Issue to Workarea
12
+
13
+ Workarea uses [GitHub Issues][] to track bugs, feature requests, and improvements to the platform. In order to report issues to Workarea, you must sign up for a GitHub account.
14
+
15
+ ### Before We Begin: Is This a Security Issue?
16
+
17
+ Please **do not** report security vulnerabilities using public GitHub issue reports (even if it's encrypted). The [security policy][] page describes the procedure to follow for reporting security issues.
18
+
19
+ ### Step 1: Determine the Source of the Bug
20
+
21
+ Workarea is a highly customizable commerce platform. As a result, when issues arise it may be difficult to ascertain their source. For example, let's say you install a plugin and immediately receive an error upon starting your application. You might think this has to do with the installed plugin, but it could in fact be something that you need to configure in your app, or a code change made at some point that conflicts with the plugin's functionality. There's a few ways of determining whether platform code is the cause of your problem...
22
+
23
+ 1. If an exception is thrown, make sure the backtrace points to lines of code in the platform. It's happening in the core platform if you see the error occurring on any file in **workarea-core**, **workarea-admin**, **workarea-testing**, or **workarea-storefront**. Otherwise, it's happening in a plugin (as long as the error doesn't stem from some decorator in your own application). Some plugins are open-source, and if they are, you can report the issue on their GitHub project. Otherwise, you'll need to [request support][] for our proprietary plugins.
24
+ 2. If you're seeing a visual distortion of some kind, or you're _not_ seeing an appended file, make sure you've **restarted your server** and clobbered assets. A lot of perceived issues with the site only happen randomly in development, and therefore are difficult to track down. It may just be that your HTTP server is moving faster than your assets can recompile. Look at your `log/development.log` file to determine where the partial or template that the error is occurring on comes from. If it's rendered from a Workarea gem, then it's happening in the core components, but otherwise it's probably something that can be fixed at the application level.
25
+ 3. For all other issues, make sure to add logging or use `require 'debug'` (or `binding.pry` if using Pry) to break into a code path and determine the state of things at that moment.
26
+
27
+ If you can prove, unequivocally, that platform code is the problem, it's a bug that you should report to the core team!
28
+
29
+ ### Step 2: Create an Executable Test Case
30
+
31
+ If you can, the best way to ensure your issue gets solved as quickly as possible is to provide a **unit test** or **integration test** that illustrates the problem, and fails in your Workarea application. Although you may write a system test if you feel it can describe the problem well, these are not preferred...instead, include an animated screenshot that goes through the problem and shows the issue in an actual application.
32
+
33
+ Due to the nature of how Workarea is required in a Ruby on Rails application, a self-executing test case cannot be provided. Instead, create a new test file that just includes the test illustrating your problem, or include a snippet of another test file that you've made changes to.
34
+
35
+ Here's an example of a test case written around a bug:
36
+
37
+ ```ruby
38
+ require 'test_helper'
39
+
40
+ module Workarea
41
+ class PackagingTest < TestCase
42
+ def test_accurate_total_value
43
+ order = create_order
44
+ product = create_product
45
+ create_pricing_sku(id: 'SKU1', prices: [{ regular: 3.to_m }])
46
+ create_pricing_sku(id: 'SKU2', prices: [{ regular: 2.5.to_m }])
47
+ order.add_item(product_id: product.id, sku: 'SKU1', quantity: 1)
48
+ order.add_item(product_id: product.id, sku: 'SKU2', quantity: 2)
49
+ Pricing.perform(order)
50
+
51
+ packaging = Packaging.new(order)
52
+ assert_equal(8.to_m, packaging.total_value)
53
+
54
+ create_order_total_discount(amount_type: :flat, amount: 1)
55
+ Pricing.perform(order)
56
+
57
+ packaging = Packaging.new(order)
58
+ assert_equal(7.to_m, packaging.total_value)
59
+ end
60
+ end
61
+ end
62
+ ```
63
+
64
+ The source of this can be pasted into a GitHub issue, which allows any Workarea contributor to copy the test into their own project and run it against a real Workarea app. This allows anyone to contribute the fix if they can figure it out!
65
+
66
+ ### Step 3: Report the issue on GitHub
67
+
68
+ To report a bug to the Workarea project, create a new issue and give it a label of "bug". When creating a new issue, you'll be prompted by a template describing the various bits of information that the platform team needs in order to help triage and solve the problem you're reporting. Be sure to include as many relevant details as possible, and to scrub sensitive information from screenshots, code examples, and links to your project. When using Workarea Hosting, keep in mind that Workarea engineers (and anyone else in the world willing to contribute to the platform) cannot access the QA/Staging environments of your projects, so screenshots are absolutely necessary if you wish to demonstrate some kind of broken functionality on your project.
69
+
70
+ Keep in mind that you can use [Markdown][] to paste highlighted code, style your issue so it's easier to read, and add emphasis or inline example images.
71
+
72
+ [GitHub Issues]: https://github.com/workarea-commerce/workarea/issues
73
+ [security policy]: security-policy.html
74
+ [request support]: https://support.workarea.com
75
+ [Markdown]: http://daringfireball.net/projects/markdown/
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: Ruby Coding Standards
3
+ excerpt: Ruby code in the Workarea platform generally follows the Ruby Style Guide.
4
+ ---
5
+
6
+ # Ruby Coding Standards
7
+
8
+ Ruby code in the Workarea platform generally follows the [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide).
9
+
10
+
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Run Sidekiq in a Local Environment
3
+ excerpt: By default, Workarea applications are configured to run Sidekiq inline for local development. In some cases, however, it could be desirable to have an application run with Sidekiq processing jobs in the background to better match a live, production environment.
4
+ ---
5
+
6
+ # Run Sidekiq in a Local Environment
7
+
8
+ By default, Workarea applications are configured to run Sidekiq inline for local development. In some cases, however, it could be desirable to have an application run with Sidekiq processing jobs in the background to better match a live, production environment.
9
+
10
+ To achieve this, there are number of steps that need to be taken to allow the development environment to function in this way. The first is modifying the environment configuration that is added for development by the Workarea app template. In `config/environments/development.rb`, you will see the line below
11
+
12
+ ```ruby
13
+ # Run Sidekiq tasks synchronously so that Sidekiq is not required in Development
14
+ require 'sidekiq/testing/inline'
15
+ ```
16
+
17
+ Remove this line or comment it out. This stops the application from running Sidekiq jobs inline during the execution of a web request. Without this line, the application will add jobs to redis for the Sidekiq process to find and process. To run sidekiq, you will need to open a terminal, navigate to your application's directory, and start the sidekiq process, exactly as you would start a web server for the application itself.
18
+
19
+ ```bash
20
+ $ cd path/to/your/app
21
+ $ bundle exec sidekiq
22
+ ```
23
+
24
+ When the command executes you will see a message that sidekiq has started. This window must remain open and running for sidekiq to continue to function. If you prefer, you can run sidekiq as a daemon with the `-d` or `--daemon` flag when starting the process.
25
+
26
+
27
+ ## Run Sidekiq in a Docker Environment
28
+
29
+ When using Docker for Workarea development, the generated `docker-compose.yml` file provides a commented out service you can use to run Sidekiq in a separate container. Just uncomment the following lines and run `docker-compose up`.
30
+
31
+ ```yaml
32
+ # sidekiq:
33
+ # <<: *web
34
+ # command: ['sh', './docker-wait.sh', 'sidekiq']
35
+ # depends_on:
36
+ # - web
37
+ # ports: []
38
+ ```
39
+
40
+ **Note**: Docker environments still require you to remove the `require` shown above from your `config/environments/development.rb` file.
@@ -0,0 +1,1005 @@
1
+ ---
2
+ title: Search
3
+ excerpt: Workarea applications persist model data to MongoDB, using it as the database of record. However, to provide near-real-time search, Workarea persists many of the same models to Elasticsearch, after first transforming them into a format suitable for se
4
+ ---
5
+
6
+ # Search
7
+
8
+ Workarea applications persist model data to MongoDB, using it as the database of record. However, to provide near-real-time search, Workarea persists many of the same models to Elasticsearch, after first transforming them into a format suitable for search.
9
+
10
+ Workarea [callbacks workers](workers.html#callbacks-worker) enqueue in response to changes to Mongoid documents, and when run, these workers synchronize these data changes to Elasticsearch.
11
+
12
+ Workarea also provides query classes which encapsulate the complexity of the various search requests to Elasticsearch needed by Workarea applications. Each search query provides the search results for a given set of query parameters. Results are Mongoid models, initialized directly from a serialized copy in Elasticsearch, so it is not necessary to query MongoDB for results.
13
+
14
+ ## Client & Server(s)
15
+
16
+ Workarea uses a [Ruby client](http://www.rubydoc.info/gems/elasticsearch-transport/5.0.4/Elasticsearch/Transport/Client) to communicate with an Elasticsearch [cluster](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/_basic_concepts.html#_cluster). Workarea Hosting provisions each cloud environment with a cluster. You must provision your own cluster for local environments.
17
+
18
+ The client instance, accessed as `Workarea.elasticsearch`, provides a [Ruby implementation](http://www.rubydoc.info/gems/elasticsearch-api/5.0.4/Elasticsearch/API) of the [Elasticsearch REST APIs](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/index.html).
19
+
20
+ ```ruby
21
+ Workarea.elasticsearch.class
22
+ # => Elasticsearch::Transport::Client
23
+ ```
24
+
25
+ In normal operation, you will not use the client directly (favoring higher level APIs), however, direct access to the client is useful for debugging and other secondary use cases. The example below uses the client to print a simple status report for the entire cluster.
26
+
27
+ ```ruby
28
+ puts Workarea.elasticsearch.cat.health(v: true, h: %w(cluster status))
29
+ # cluster status
30
+ # elasticsearch yellow
31
+ ```
32
+
33
+ ## Documents, Indexes, Types & Mappings
34
+
35
+ ### Types
36
+
37
+ Elasticsearch uses [types](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/_basic_concepts.html#_type) to categorize documents according to their fields, or [mappings](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/mapping.html). Workarea uses four types out of the box:
38
+
39
+ - admin
40
+ - help
41
+ - storefront
42
+ - category
43
+
44
+ Workarea uses documents of type _category_ to index category queries for use with the [percolate query](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/query-dsl-percolate-query.html), which can find the matching categories for a given product document. Because category documents are indexed and queried differently than the others, I do not cover them in this guide.
45
+
46
+ ### Indexes
47
+
48
+ Documents of type _admin_, _help_, and _storefront_ are stored in separate indexes. Workarea applications use a varying number of Elasticsearch indexes. An index exists for each combination of site name, Rails environment, locale, and Elasticsearch document type. <sup><a href="#notes" id="note-1-context">[1]</a></sup>
49
+
50
+ For example, the indexes for a simple development application could be as follows.
51
+
52
+ - board\_games\_direct\_development\_en\_admin
53
+ - board\_games\_direct\_development\_en\_help
54
+ - board\_games\_direct\_development\_en\_storefront
55
+
56
+ Meanwhile, the following list of indexes could be used in an application with multiple site names (requires the [Multi Site](https://stash.tools.weblinc.com/projects/WL/repos/workarea-multi-site/browse) plugin), environments, and locales.
57
+
58
+ - board\_games\_direct\_development\_en\_admin
59
+ - board\_games\_direct\_development\_en\_help
60
+ - board\_games\_direct\_development\_en\_storefront
61
+ - board\_games\_direct\_development\_es\_admin
62
+ - board\_games\_direct\_development\_es\_help
63
+ - board\_games\_direct\_development\_es\_storefront
64
+ - board\_games\_direct\_production\_en\_admin
65
+ - board\_games\_direct\_production\_en\_help
66
+ - board\_games\_direct\_production\_en\_storefront
67
+ - board\_games\_direct\_production\_es\_admin
68
+ - board\_games\_direct\_production\_es\_help
69
+ - board\_games\_direct\_production\_es\_storefront
70
+ - board\_games\_direct\_qa\_en\_admin
71
+ - board\_games\_direct\_qa\_en\_help
72
+ - board\_games\_direct\_qa\_en\_storefront
73
+ - board\_games\_direct\_qa\_es\_admin
74
+ - board\_games\_direct\_qa\_es\_help
75
+ - board\_games\_direct\_qa\_es\_storefront
76
+ - board\_games\_direct\_staging\_en\_admin
77
+ - board\_games\_direct\_staging\_en\_help
78
+ - board\_games\_direct\_staging\_en\_storefront
79
+ - board\_games\_direct\_staging\_es\_admin
80
+ - board\_games\_direct\_staging\_es\_help
81
+ - board\_games\_direct\_staging\_es\_storefront
82
+ - party\_games\_direct\_development\_en\_admin
83
+ - party\_games\_direct\_development\_en\_help
84
+ - party\_games\_direct\_development\_en\_storefront
85
+ - party\_games\_direct\_development\_es\_admin
86
+ - party\_games\_direct\_development\_es\_help
87
+ - party\_games\_direct\_development\_es\_storefront
88
+ - party\_games\_direct\_production\_en\_admin
89
+ - party\_games\_direct\_production\_en\_help
90
+ - party\_games\_direct\_production\_en\_storefront
91
+ - party\_games\_direct\_production\_es\_admin
92
+ - party\_games\_direct\_production\_es\_help
93
+ - party\_games\_direct\_production\_es\_storefront
94
+ - party\_games\_direct\_qa\_en\_admin
95
+ - party\_games\_direct\_qa\_en\_help
96
+ - party\_games\_direct\_qa\_en\_storefront
97
+ - party\_games\_direct\_qa\_es\_admin
98
+ - party\_games\_direct\_qa\_es\_help
99
+ - party\_games\_direct\_qa\_es\_storefront
100
+ - party\_games\_direct\_staging\_en\_admin
101
+ - party\_games\_direct\_staging\_en\_help
102
+ - party\_games\_direct\_staging\_en\_storefront
103
+ - party\_games\_direct\_staging\_es\_admin
104
+ - party\_games\_direct\_staging\_es\_help
105
+ - party\_games\_direct\_staging\_es\_storefront
106
+
107
+ ### Mappings
108
+
109
+ Elasticsearch mappings are typically declared for an index when the index is created, however, the mapping may be extended at index time, such as when an index's mapping includes [dynamic templates](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html).
110
+
111
+ When creating a new index, Workarea looks for a configuration value declaring the mapping for that index. The configuration keys are named after the different document types. <sup><a href="#notes" id="note-2-context">[2]</a></sup> Each configured mapping includes _properties_ and _dynamic templates_.
112
+
113
+ For example, the default configuration of the mappings for _storefront_ indexes are shown below.
114
+
115
+ ```ruby
116
+ config.elasticsearch_mappings.storefront = {
117
+ category: { properties: { query: { type: 'percolator' } } },
118
+ storefront: {
119
+ dynamic_templates: [
120
+ {
121
+ facets: {
122
+ path_match: 'facets.*',
123
+ mapping: { type: 'keyword' }
124
+ }
125
+ },
126
+ {
127
+ numeric: {
128
+ path_match: 'numeric.*',
129
+ mapping: { type: 'float' }
130
+ }
131
+ },
132
+ {
133
+ keywords: {
134
+ path_match: 'keywords.*',
135
+ mapping: { type: 'keyword' }
136
+ }
137
+ },
138
+ {
139
+ sorts: {
140
+ path_match: 'sorts.*',
141
+ mapping: { type: 'float' }
142
+ }
143
+ },
144
+ {
145
+ content: {
146
+ path_match: 'content.*',
147
+ mapping: { type: 'text', analyzer: 'text_analyzer' }
148
+ }
149
+ },
150
+ {
151
+ cache: {
152
+ path_match: 'cache.*',
153
+ mapping: { index: false }
154
+ }
155
+ }
156
+ ],
157
+ properties: {
158
+ id: { type: 'keyword' },
159
+ type: { type: 'keyword' },
160
+ slug: { type: 'keyword' },
161
+ suggestion_content: { type: 'string', analyzer: 'text_analyzer' }
162
+ }
163
+ }
164
+ }
165
+ ```
166
+
167
+ Because the storefront mappings depend heavily on dynamic templates, the method `Workarea::Search::Storefront.ensure_dynamic_mappings` is available, which creates a product document, indexes it, and then removes it. This process helps reduce errors in a new environment where the index-time mappings have not been created yet.
168
+
169
+ ### Document Interface
170
+
171
+ The `Workarea::Elasticsearch::Document` module provides a Ruby interface to represent behavior shared by Elasticsearch documents of _all_ types. Each class that includes this module represents a _specific_ type. Calling `Document.all` returns a list of the more specific classes.
172
+
173
+ ```
174
+ puts Workarea::Elasticsearch::Document.all
175
+ # Workarea::Search::Admin
176
+ # Workarea::Search::Help
177
+ # Workarea::Search::Storefront
178
+ ```
179
+
180
+ The classes listed above are used almost exclusively for _type_ and _index_ level concerns (see below), while the descendants of these classes (covered under Search Models) are used primarily for _document_ level concerns. <sup><a href="#notes" id="note-3-context">[3]</a></sup>
181
+
182
+ These classes respond to `.type` and `.mappings`, which describe the type of documents for which they are responsible. (Note that `.mappings` returns the Workarea configuration, not the actual mappings on the index.)
183
+
184
+ ```ruby
185
+ Workarea::Elasticsearch::Document.all.map(&:type)
186
+ # => [:admin, :help, :storefront]
187
+
188
+ pp Workarea::Search::Help.mappings
189
+ # {:help=>
190
+ # {:dynamic_templates=>
191
+ # [{:facet_values=>
192
+ # {:path_match=>"facets.*",
193
+ # :mapping=>{:type=>"string", :analyzer=>"keyword"}}}],
194
+ # :properties=>
195
+ # {:id=>{:type=>"string", :index=>"not_analyzed"},
196
+ # :name=>{:type=>"string", :analyzer=>"text_analyzer"},
197
+ # :body=>{:type=>"string", :analyzer=>"text_analyzer"},
198
+ # :created_at=>{:type=>"date"}}}}
199
+ ```
200
+
201
+ The following methods are used to create, delete, and reset all indexes for the particular document type. Remember that each document type may be stored on many indexes depending on the number of sites, environments, and locales.
202
+
203
+ - `.create_indexes!`
204
+ - `.delete_indexes!`
205
+ - `.reset_indexes!`
206
+
207
+ Every Elasticsearch document class has a <dfn>current index</dfn>. The example below demonstrates the current index is determined by the combination of current site name, Rails environment, locale, and document type.
208
+
209
+ ```ruby
210
+ Workarea::Search::Storefront.current_index.name
211
+ # => "board_games_direct_development_en_storefront"
212
+
213
+ Workarea::Search::Storefront.current_index.url
214
+ # => "http://localhost:9200/board_games_direct_development_en_storefront"
215
+
216
+ Workarea.config.site_name
217
+ # => "Board Games Direct"
218
+
219
+ Rails.env
220
+ # => "development"
221
+
222
+ I18n.locale
223
+ # => :en
224
+
225
+ Workarea::Search::Storefront.type
226
+ # => :storefront
227
+ ```
228
+
229
+ The current index is an instance of `Workarea::Elasticsearch::Index`, another abstraction provided by Workarea.
230
+
231
+ ```ruby
232
+ Workarea::Search::Storefront.current_index.class
233
+ # => Workarea::Elasticsearch::Index
234
+ ```
235
+
236
+ I do not cover this abstraction in detail because it is used internally and rarely invoked by application code. A Search model initializes instances of `Index` when needed and delegates index operations to its `current_index`.
237
+
238
+ ## Indexing
239
+
240
+ Most search indexing is performed by [callbacks workers](workers.html#callbacks-worker) running in response to changes to application documents, and by workers run [on a schedule](workers.html#sidekiq-cron-job). Internally, workers use search models to transform the affected application documents into search documents, which involves serializing in-memory objects into strings. The search models then index the transformed documents into Elasticsearch.
241
+
242
+ To a lesser extent, rake tasks and seeds are also used to index documents for search.
243
+
244
+ ### Workers
245
+
246
+ Various workers are used to index documents into Elasticsearch. Many of these are callbacks workers that run in response to life cycle changes on application documents (Mongoid documents) and use search models to forward Mongoid changes to Elasticsearch.
247
+
248
+ For example, review the implementation of `Workarea::IndexPage`, below.
249
+
250
+ ```ruby
251
+ module Workarea
252
+ class IndexPage
253
+ include Sidekiq::Worker
254
+ include Sidekiq::CallbacksWorker
255
+
256
+ sidekiq_options(
257
+ enqueue_on: { Content::Page => [:save, :destroy] },
258
+ unique: :until_executing
259
+ )
260
+
261
+ def perform(id)
262
+ page = Content::Page.find(id)
263
+ Search::Storefront::Page.new(page).save
264
+ rescue Mongoid::Errors::DocumentNotFound
265
+ Search::Storefront::Page.new(
266
+ Content::Page.new(id: id)
267
+ ).destroy
268
+ end
269
+ end
270
+ end
271
+ ```
272
+
273
+ when a `Workarea::Content::Page` is saved or destroyed, `Workarea::IndexPage` is enqueued with the id of the saved or destroyed content page. When this worker runs, it looks up the affected content page and creates an instance of `Search::Storefront::Page` from it. Then the worker uses `Search::Storefront::Page#save` or `Search::Storefront::Page#destroy` to create and index, or delete, the corresponding search document.
274
+
275
+ The `Workarea::IndexAdminSearch` worker follows the same pattern, but it is enqueued on _save_, _touch_, or _destroy_ of any application document. The worker uses the `Search::Admin.for` factory to initialize the correct search model from the affected application document.
276
+
277
+ The Admin UI inlines `IndexAdminSearch` for the duration of each Admin request so that model changes applied through the Admin UI are applied inline rather than asynchronously.
278
+
279
+ ```ruby
280
+ module Workarea
281
+ module Admin
282
+ class ApplicationController < Workarea::ApplicationController
283
+ # ...
284
+ around_action :inline_search_indexing
285
+
286
+ # ...
287
+
288
+ private
289
+
290
+ # ...
291
+
292
+ def inline_search_indexing
293
+ Sidekiq::Callbacks.inline(IndexAdminSearch) { yield }
294
+ end
295
+
296
+ # ...
297
+ end
298
+ end
299
+ end
300
+ ```
301
+
302
+ Some callbacks workers delegate to other workers rather than using a search model directly. The workers that are delegated _to_ implement a `.perform` class method, which typically receives the affected Mongoid model instance (rather than an id) as its argument.
303
+
304
+ For example, changes to catalog variants and product images cause the parent products to be re-indexed into the Storefront indexes. To do so, the `Workarea::IndexProductChildren` worker determines which parent product is affected and uses `IndexProduct.perform` to re-index the product.
305
+
306
+ ```ruby
307
+ module Workarea
308
+ class IndexProductChildren
309
+ include Sidekiq::Worker
310
+ include Sidekiq::CallbacksWorker
311
+
312
+ sidekiq_options(
313
+ enqueue_on: {
314
+ Catalog::Variant => [:save, :destroy],
315
+ Catalog::ProductImage => [:save, :destroy],
316
+ with: -> { [_parent.id.to_s] }
317
+ },
318
+ unique: :until_executing
319
+ )
320
+
321
+ def perform(id)
322
+ product = Catalog::Product.find(id) rescue nil
323
+ IndexProduct.perform(product) if product.present?
324
+ end
325
+ end
326
+ end
327
+ ```
328
+
329
+ Similarly, changes to catalog categories cause affected products to be re-indexed. The `Workarea::IndexCategoryChanges` determines which products are affected and uses `BulkIndexProducts.perform` to re-index them in a single request.
330
+
331
+ ```ruby
332
+ module Workarea
333
+ class IndexCategoryChanges
334
+ include Sidekiq::Worker
335
+ include Sidekiq::CallbacksWorker
336
+
337
+ sidekiq_options(
338
+ enqueue_on: { Catalog::Category => :save, with: -> { [changes] } },
339
+ ignore_if: -> { changes['product_ids'].blank? },
340
+ unique: :until_executing
341
+ )
342
+
343
+ def perform(changes)
344
+ if changes['product_ids'].present?
345
+ previous_ids = changes['product_ids'].first || []
346
+ new_ids = changes['product_ids'].second || []
347
+
348
+ require_index_ids = (previous_ids - new_ids) + (new_ids - previous_ids)
349
+ BulkIndexProducts.perform(require_index_ids)
350
+ end
351
+ end
352
+ end
353
+ end
354
+ ```
355
+
356
+ Other indexing workers run on a schedule instead of in response to model changes. Examples are `Workarea::CleanOrders` and `Workarea::KeepProductIndexFresh`.
357
+
358
+ Finally, some workers are run neither as callbacks nor on a schedule. These workers must be run manually, usually from another worker. Examples are `Workarea::BulkIndexProducts` and `Workarea::BulkIndexSearches`.
359
+
360
+ ### Search Models
361
+
362
+ Search models are initialized from Mongoid documents and can save and destroy corresponding search documents to and from relevant Elasticsearch indexes. Search documents are responsible for transforming Mongoid documents into search documents, and in the process of doing so, they serialize the entire Mongoid document for storage into Elasticsearch.
363
+
364
+ #### Initializing
365
+
366
+ `Workarea::Search::Admin` is an abstract superclass whose subclasses are search models that index documents of type _admin_.
367
+
368
+ ```ruby
369
+ puts Workarea::Search::Admin.descendants
370
+ # Workarea::Search::Admin::CatalogCategory
371
+ # Workarea::Search::Admin::CatalogProduct
372
+ # Workarea::Search::Admin::Content
373
+ # Workarea::Search::Admin::ContentAsset
374
+ # Workarea::Search::Admin::ContentPage
375
+ # Workarea::Search::Admin::InventorySku
376
+ # Workarea::Search::Admin::Navigation
377
+ # Workarea::Search::Admin::NavigationMenu
378
+ # Workarea::Search::Admin::Order
379
+ # Workarea::Search::Admin::PaymentTransaction
380
+ # Workarea::Search::Admin::PricingDiscount
381
+ # Workarea::Search::Admin::PricingSku
382
+ # Workarea::Search::Admin::Release
383
+ # Workarea::Search::Admin::User
384
+ ```
385
+
386
+ The factory method `Workarea::Search::Admin.for` will create an instance of one of the above search models for a given Mongoid model (see example in workers section, above).
387
+
388
+ Similarly, `Workarea::Search::Storefront` is an abstract superclass whose subclasses are search models that index documents of type _storefront_.
389
+
390
+ ```ruby
391
+ puts Workarea::Search::Storefront.descendants
392
+ # Workarea::Search::Storefront::Category
393
+ # Workarea::Search::Storefront::Page
394
+ # Workarea::Search::Storefront::Product
395
+ # Workarea::Search::Storefront::Search
396
+ ```
397
+
398
+ `Workarea::Search::Help` has no descendants and is used as the search model for documents of type _help_.
399
+
400
+ ```ruby
401
+ Workarea::Search::Help.descendants
402
+ # => []
403
+ ```
404
+
405
+ Initialize a search model with an application document (a Mongoid model).
406
+
407
+ ```ruby
408
+ product = Workarea::Catalog::Product.create!(name: 'Escape the Room')
409
+
410
+ product_admin_search_model = Workarea::Search::Admin::CatalogProduct.new(product)
411
+
412
+ # or use the factory for 'admin' documents
413
+ product_admin_search_model = Workarea::Search::Admin.for(product)
414
+
415
+ product_admin_search_model.class
416
+ # => Workarea::Search::Admin::CatalogProduct
417
+ ```
418
+
419
+ Search models return a `type`, which describes the Mongoid document type rather than the Elasticsearch document type. In the case of Admin and Storefront search models, many different Mongoid types map to the same Elasticsearch types.
420
+
421
+ ```ruby
422
+ # Mongoid "type"
423
+ product_admin_search_model.type
424
+ # => "product"
425
+
426
+ # Elasticsearch "type"
427
+ product_admin_search_model.class.type
428
+ # => :admin
429
+ ```
430
+
431
+ The following examples use the same Mongoid model from above to create and explore a Storefront search model.
432
+
433
+ ```ruby
434
+ product_storefront_search_model = Workarea::Search::Storefront::Product.new(product)
435
+
436
+ product_storefront_search_model.class
437
+ # => Workarea::Search::Storefront::Product
438
+
439
+ product_storefront_search_model.type
440
+ # => "product"
441
+
442
+ product_storefront_search_model.class.type
443
+ # => :storefront
444
+ ```
445
+
446
+ A search model provides access to the original Mongoid model.
447
+
448
+ ```ruby
449
+ product_storefront_search_model.model.class
450
+ # => Workarea::Catalog::Product
451
+
452
+ product_storefront_search_model.model.name
453
+ # => "Escape the Room"
454
+
455
+ product_storefront_search_model.model.id
456
+ # => "273E095F5A"
457
+ ```
458
+
459
+ Be aware that a search model and its originating Mongoid model have different, albeit similar, IDs.
460
+
461
+ ```ruby
462
+ product_storefront_search_model.id
463
+ # => "product-273E095F5A"
464
+
465
+ product_storefront_search_model.catalog_id
466
+ # => "273E095F5A"
467
+
468
+ product_storefront_search_model.model.id
469
+ # => "273E095F5A"
470
+ ```
471
+
472
+ #### Saving & Destroying
473
+
474
+ Use the `save` method to create and index a search document, and use the `destroy` method to delete a search document.
475
+
476
+ ```ruby
477
+ product_storefront_search_model.save
478
+ ```
479
+
480
+ ```ruby
481
+ product_storefront_search_model.destroy
482
+ ```
483
+
484
+ These operations may affect multiple documents (in multiple indexes) if the application has multiple locales. These methods save or destroy one document per locale.
485
+
486
+ #### Creating Search Documents
487
+
488
+ When search models `save` documents, they must first create a document that is suitable for Elasticsearch. They do so by transforming the Mongoid model, and in many cases, aggregating multiple related models. For example, a searchable product requires catalog, pricing, and inventory data. Each search model implements `as_document` and `as_bulk_document` for this purpose.
489
+
490
+ The following examples create Admin and Storefront search documents from the same Mongoid source document. Notice how the fields of each search document are different.
491
+
492
+ ```ruby
493
+ pp product_admin_search_model.as_document
494
+ # {:id=>"product-273E095F5A",
495
+ # :name=>"Escape the Room",
496
+ # :facets=>
497
+ # {:status=>"inactive",
498
+ # :type=>"product",
499
+ # :tags=>[],
500
+ # :upcoming_changes=>[],
501
+ # :category=>"Automotive",
502
+ # :category_id=>
503
+ # ["5995eaf007dd42106a36021a",
504
+ # "5995eaf007dd42106a360218",
505
+ # "5995eaf007dd42106a360216",
506
+ # "5995eaf007dd42106a360220",
507
+ # "5995eaf007dd42106a360214",
508
+ # "5995eaf007dd42106a360224"],
509
+ # :on_sale=>false,
510
+ # :issues=>["No Images", "No Description", "No Variants"],
511
+ # :template=>"generic"},
512
+ # :created_at=>Wed, 23 Aug 2017 18:46:23 UTC +00:00,
513
+ # :updated_at=>Wed, 23 Aug 2017 18:46:23 UTC +00:00,
514
+ # :search_text=>["273E095F5A", "Escape the Room", "product"],
515
+ # :jump_to_text=>"Escape the Room (273E095F5A)",
516
+ # :jump_to_search_text=>["273E095F5A", "Escape the Room", "product"],
517
+ # :jump_to_position=>3,
518
+ # :jump_to_route_helper=>"catalog_product_path",
519
+ # :jump_to_param=>"escape-the-room",
520
+ # :releasable=>true}
521
+ ```
522
+
523
+ ```ruby
524
+ pp product_storefront_search_model.as_document
525
+ # {:id=>"product-273E095F5A",
526
+ # :type=>"product",
527
+ # :slug=>"escape-the-room",
528
+ # :active=>{:now=>false},
529
+ # :suggestion_content=>
530
+ # "Escape the Room Outdoors, Jewelry, Sports, Tools, Movies, Grocery ",
531
+ # :created_at=>Wed, 23 Aug 2017 18:46:23 UTC +00:00,
532
+ # :updated_at=>Wed, 23 Aug 2017 18:46:23 UTC +00:00,
533
+ # :facets=>
534
+ # {:category=>"Automotive",
535
+ # :category_id=>
536
+ # ["5995eaf007dd42106a36021a",
537
+ # "5995eaf007dd42106a360218",
538
+ # "5995eaf007dd42106a360216",
539
+ # "5995eaf007dd42106a360220",
540
+ # "5995eaf007dd42106a360214",
541
+ # "5995eaf007dd42106a360224"],
542
+ # :on_sale=>false},
543
+ # :numeric=>{:price=>[0.0], :inventory=>0, :variant_count=>0},
544
+ # :keywords=>{:catalog_id=>"273E095F5A", :sku=>[]},
545
+ # :sorts=>
546
+ # {BSON::ObjectId('5995eaf007dd42106a360214')=>nil,
547
+ # BSON::ObjectId('5995eaf007dd42106a360216')=>nil,
548
+ # BSON::ObjectId('5995eaf007dd42106a360218')=>nil,
549
+ # BSON::ObjectId('5995eaf007dd42106a36021a')=>nil,
550
+ # BSON::ObjectId('5995eaf007dd42106a360220')=>nil,
551
+ # BSON::ObjectId('5995eaf007dd42106a360224')=>nil,
552
+ # :price=>0.0,
553
+ # :orders_score=>0.0,
554
+ # :views_score=>0.0},
555
+ # :content=>
556
+ # {:name=>"Escape the Room",
557
+ # :category_names=>"Outdoors, Jewelry, Sports, Tools, Movies, Grocery",
558
+ # :description=>"",
559
+ # :details=>" ",
560
+ # :facets=>""},
561
+ # :cache=>
562
+ # {:image=>"/product_images/placeholder/small_thumb.jpg?c=1502997231",
563
+ # :pricing=>[],
564
+ # :inventory=>[]}}
565
+ ```
566
+
567
+ The fields of a search document should match the mapping for its type. Some fields appear in the mapping explicitly, as properties, while other match implicitly, via dynamic templates.
568
+
569
+ Some fields are used only for storage, not search. For example, the _cache.image_, _cache.pricing_, and _cache.inventory_ fields above are all configured to be stored but not indexed.
570
+
571
+ ```ruby
572
+ config.elasticsearch_mappings.storefront = {
573
+ # ...
574
+ storefront: {
575
+ dynamic_templates: [
576
+ # ...
577
+ {
578
+ cache: {
579
+ path_match: 'cache.*',
580
+ mapping: { index: false }
581
+ }
582
+ }
583
+ ],
584
+ properties: {
585
+ # ...
586
+ }
587
+ }
588
+ }
589
+ ```
590
+
591
+ Cached fields are used when loading results to avoid additional queries to MongoDB.
592
+
593
+ Much of each search model's implementation is made up of methods used to compose the hash returned from _as\_document_.
594
+
595
+ #### Transformation "Helpers"
596
+
597
+ Workarea provides several classes to help construct the `as_document` hash.
598
+
599
+ `Workarea::Search::Admin::Releasable` is included in search models for releasable models. It includes additional fields in the `as_document` hash related to releases. The following example lists classes that include this module.
600
+
601
+ ```ruby
602
+ releasables = Workarea::Search::Admin.descendants.select do |klass|
603
+ klass.included_modules.include?(Workarea::Search::Admin::Releasable)
604
+ end
605
+ puts releasables.map(&:to_s).sort
606
+ # Workarea::Search::Admin::CatalogCategory
607
+ # Workarea::Search::Admin::CatalogProduct
608
+ # Workarea::Search::Admin::Content
609
+ # Workarea::Search::Admin::ContentPage
610
+ # Workarea::Search::Admin::NavigationMenu
611
+ # Workarea::Search::Admin::PricingDiscount
612
+ # Workarea::Search::Admin::PricingSku
613
+ ```
614
+
615
+ Several other <abbr title="plain old Ruby object">PORO</abbr>s exist to help construct specific values for the as\_document hash.
616
+
617
+ - `Workarea::Search::FacetValues`
618
+ - `Workarea::Search::HashText`
619
+ - `Workarea::Search::OrderText`
620
+ - `Workarea::Search::UserText`
621
+
622
+ ### Serialization & De-Serialization
623
+
624
+ Workarea provides `Workarea::Elasticsearch::Serializer` for serializing in-memory models into strings suitable for storage in Elasticsearch, and for de-serializing those strings back into models without needing to query MongoDB.
625
+
626
+ ```ruby
627
+ product = Workarea::Catalog::Product.first
628
+
629
+ product.class
630
+ # => Workarea::Catalog::Product
631
+
632
+ product.id
633
+ # => "006CBBCD90"
634
+
635
+ product.name
636
+ # => "Fantastic Linen Pants"
637
+ ```
638
+
639
+ Serialization passes the object through `Mongoid::Document.as_document` (if a Mongoid document), then `Marshal.dump`, and finally `Base64.encode64`.
640
+
641
+ ```ruby
642
+ serialized_product = Workarea::Elasticsearch::Serializer.serialize(product)
643
+
644
+ serialized_product.class
645
+ # => Hash
646
+
647
+ serialized_product['model_class']
648
+ # => "Workarea::Catalog::Product"
649
+
650
+ serialized_product['model']
651
+ # => "BAhDOhNCU09OOjpEb2N1bWVudHsVSSIIX2lkBjoGRVRJIg8wMDZDQkJDRDkw...
652
+ ```
653
+
654
+ De-serialization reverses the process, passing the string through `Base64.decode64`, then `Marshal.load`, and finally `Mongoid::Factory.from_db` to re-create the Mongoid document instance.
655
+
656
+ ```ruby
657
+ deserialized_product = Workarea::Elasticsearch::Serializer.deserialize(serialized_product)
658
+
659
+ deserialized_product.class
660
+ # => Workarea::Catalog::Product
661
+
662
+ deserialized_product.id
663
+ # => "006CBBCD90"
664
+
665
+ deserialized_product.name
666
+ # => "Fantastic Linen Pants"
667
+ ```
668
+
669
+ ### Rake Tasks
670
+
671
+ Workarea provides several Rake tasks to manually re-index all search indexes or indexes for particular document types. The tasks are defined in `workarea-core/lib/tasks/search.rake`.
672
+
673
+ You can run the tasks as commands.
674
+
675
+ ```bash
676
+ $ bin/rails workarea::search_index::all
677
+ ```
678
+
679
+ ```bash
680
+ $ bin/rails workarea::search_index::admin
681
+ ```
682
+
683
+ ```bash
684
+ $ bin/rails workarea::search_index::storefront
685
+ ```
686
+
687
+ ```bash
688
+ $ bin/rails workarea::search_index::help
689
+ ```
690
+
691
+ Or invoke the tasks programatically.
692
+
693
+ ```ruby
694
+ Rake::Task['workarea:search_index:all'].invoke
695
+ ```
696
+
697
+ ```ruby
698
+ Rake::Task['workarea:search_index:admin'].invoke
699
+ ```
700
+
701
+ ```ruby
702
+ Rake::Task['workarea:search_index:storefront'].invoke
703
+ ```
704
+
705
+ ```ruby
706
+ Rake::Task['workarea:search_index:help'].invoke
707
+ ```
708
+
709
+ Regardless of how you execute them, these tasks each inline all Sidekiq workers for the duration of the task. As of Workarea 3.3, you can prevent this behavior by setting the environment variable `INLINE` to `false`. For example:
710
+
711
+ ```bash
712
+ $ INLINE=false bin/rails workarea::search_index::all
713
+ ```
714
+
715
+ ### Seeds
716
+
717
+ Running seeds drops all MongoDB and Elasticsearch data for the current environment and primes both databases with data. Running seeds therefore resets all Elasticsearch indexes.
718
+
719
+ You can run seeds as a command.
720
+
721
+ ```bash
722
+ $ bin/rails db:seed
723
+ ```
724
+
725
+ Or programatically.
726
+
727
+ ```ruby
728
+ Workarea::Seeds.run
729
+ ```
730
+
731
+ ## Search Queries
732
+
733
+ Workarea provides a variety of query classes which are responsible for complicated reads. This includes Elasticsearch searches, for which Workarea provides a variety of search queries.
734
+
735
+ Search queries are initialized with params and construct and perform an [Elasticsearch request body search](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/search-request-body.html). The query provides access to the raw Elasticsearch response in addition to "loaded" results, which returns the results as Mongoid documents, initialized from the serialized model cache within each Elasticsearch document.
736
+
737
+ Each search query instance therefore represents the Elasticsearch request and response for a given set of params. UI code often wraps a query instance in a view model and presents the results.
738
+
739
+ ### Types & Initialization
740
+
741
+ Search queries are classes that include `Workarea::Search::Query`. The following example introspects the Ruby object space for search query classes.
742
+
743
+ ```ruby
744
+ searches = ObjectSpace.each_object(Class).select do |klass|
745
+ klass.included_modules.include?(Workarea::Search::Query)
746
+ end
747
+ puts searches.map(&:to_s).sort
748
+ # Workarea::Search::AdminAssets
749
+ # Workarea::Search::AdminCategories
750
+ # Workarea::Search::AdminDiscounts
751
+ # Workarea::Search::AdminInventorySkus
752
+ # Workarea::Search::AdminOrders
753
+ # Workarea::Search::AdminPages
754
+ # Workarea::Search::AdminPaymentTransactions
755
+ # Workarea::Search::AdminPricingSkus
756
+ # Workarea::Search::AdminProducts
757
+ # Workarea::Search::AdminReleasables
758
+ # Workarea::Search::AdminSearch
759
+ # Workarea::Search::AdminUsers
760
+ # Workarea::Search::Categorization
761
+ # Workarea::Search::CategoryBrowse
762
+ # Workarea::Search::HelpSearch
763
+ # Workarea::Search::ProductSearch
764
+ # Workarea::Search::RelatedHelp
765
+ # Workarea::Search::RelatedProducts
766
+ # Workarea::Search::SearchSuggestions
767
+ ```
768
+
769
+ Each search query searches for results of a particular Elasticsearch document type. The following example groups the search queries by type.
770
+
771
+ ```ruby
772
+ pp searches.group_by(&:document)
773
+ # {Workarea::Search::Storefront=>
774
+ # [Workarea::Search::SearchSuggestions,
775
+ # Workarea::Search::RelatedProducts,
776
+ # Workarea::Search::ProductSearch,
777
+ # Workarea::Search::CategoryBrowse,
778
+ # Workarea::Search::Categorization],
779
+ # Workarea::Search::Help=>
780
+ # [Workarea::Search::RelatedHelp,
781
+ # Workarea::Search::HelpSearch],
782
+ # Workarea::Search::Admin=>
783
+ # [Workarea::Search::AdminUsers,
784
+ # Workarea::Search::AdminSearch,
785
+ # Workarea::Search::AdminReleasables,
786
+ # Workarea::Search::AdminProducts,
787
+ # Workarea::Search::AdminPricingSkus,
788
+ # Workarea::Search::AdminPaymentTransactions,
789
+ # Workarea::Search::AdminPages,
790
+ # Workarea::Search::AdminOrders,
791
+ # Workarea::Search::AdminInventorySkus,
792
+ # Workarea::Search::AdminDiscounts,
793
+ # Workarea::Search::AdminCategories,
794
+ # Workarea::Search::AdminAssets]}
795
+ ```
796
+
797
+ Each search query instance is initialized with params.
798
+
799
+ ```ruby
800
+ product_admin_search_query = Workarea::Search::AdminProducts.new(q: 'escape')
801
+
802
+ product_admin_search_query.class
803
+ # => Workarea::Search::AdminProducts
804
+
805
+ product_admin_search_query.class.document
806
+ # => Workarea::Search::Admin
807
+
808
+ product_admin_search_query.class.document.type
809
+ # => :admin
810
+ ```
811
+
812
+ ### Results
813
+
814
+ The `#total` and `#stats` methods return meta data about the results.
815
+
816
+ ```ruby
817
+ product_admin_search_query.total
818
+ # => 1
819
+
820
+ pp product_admin_search_query.stats
821
+ # {"upcoming_changes"=>
822
+ # {"doc_count_error_upper_bound"=>0, "sum_other_doc_count"=>0, "buckets"=>[]},
823
+ # "template"=>
824
+ # {"doc_count_error_upper_bound"=>0,
825
+ # "sum_other_doc_count"=>0,
826
+ # "buckets"=>[{"key"=>"generic", "doc_count"=>1}]},
827
+ # "color"=>
828
+ # {"doc_count_error_upper_bound"=>0, "sum_other_doc_count"=>0, "buckets"=>[]},
829
+ # "size"=>
830
+ # {"doc_count_error_upper_bound"=>0, "sum_other_doc_count"=>0, "buckets"=>[]},
831
+ # "price"=>
832
+ # {"buckets"=>
833
+ # [{"key"=>"*-9.99", "to"=>9.99, "doc_count"=>0},
834
+ # {"key"=>"10.0-19.99", "from"=>10.0, "to"=>19.99, "doc_count"=>0},
835
+ # {"key"=>"20.0-29.99", "from"=>20.0, "to"=>29.99, "doc_count"=>0},
836
+ # {"key"=>"30.0-39.99", "from"=>30.0, "to"=>39.99, "doc_count"=>0},
837
+ # {"key"=>"40.0-49.99", "from"=>40.0, "to"=>49.99, "doc_count"=>0},
838
+ # {"key"=>"50.0-59.99", "from"=>50.0, "to"=>59.99, "doc_count"=>0},
839
+ # {"key"=>"60.0-69.99", "from"=>60.0, "to"=>69.99, "doc_count"=>0},
840
+ # {"key"=>"70.0-79.99", "from"=>70.0, "to"=>79.99, "doc_count"=>0},
841
+ # {"key"=>"80.0-89.99", "from"=>80.0, "to"=>89.99, "doc_count"=>0},
842
+ # {"key"=>"90.0-99.99", "from"=>90.0, "to"=>99.99, "doc_count"=>0},
843
+ # {"key"=>"100.0-*", "from"=>100.0, "doc_count"=>0}]},
844
+ # "type"=>
845
+ # {"doc_count_error_upper_bound"=>0,
846
+ # "sum_other_doc_count"=>0,
847
+ # "buckets"=>[{"key"=>"product", "doc_count"=>1}]},
848
+ # "issues"=>
849
+ # {"doc_count_error_upper_bound"=>0,
850
+ # "sum_other_doc_count"=>0,
851
+ # "buckets"=>
852
+ # [{"key"=>"No Description", "doc_count"=>1},
853
+ # {"key"=>"No Images", "doc_count"=>1},
854
+ # {"key"=>"No Variants", "doc_count"=>1}]},
855
+ # "status"=>
856
+ # {"doc_count_error_upper_bound"=>0,
857
+ # "sum_other_doc_count"=>0,
858
+ # "buckets"=>[{"key"=>"inactive", "doc_count"=>1}]},
859
+ # "tags"=>
860
+ # {"doc_count_error_upper_bound"=>0, "sum_other_doc_count"=>0, "buckets"=>[]}}
861
+ ```
862
+
863
+ The `#response` method returns the raw response. Looking at the response, you can see the serialized model data stored within the Elasticsearch document source.
864
+
865
+ ```ruby
866
+ pp product_admin_search_query.response
867
+ # {"took"=>23,
868
+ # "timed_out"=>false,
869
+ # "_shards"=>{"total"=>5, "successful"=>5, "failed"=>0},
870
+ # "hits"=>
871
+ # {"total"=>1,
872
+ # "max_score"=>nil,
873
+ # "hits"=>
874
+ # [{"_index"=>"try_search_development_en_admin",
875
+ # "_type"=>"admin",
876
+ # "_id"=>"product-273E095F5A",
877
+ # "_score"=>6.044283,
878
+ # "_source"=>
879
+ # {"id"=>"product-273E095F5A",
880
+ #
881
+ # # ...
882
+ #
883
+ # "model_class"=>"Workarea::Catalog::Product",
884
+ # "model"=>
885
+ # "BAhDOhNCU09OOjpEb2N1bWVudHsSSSIIX2lkBjoGRVRJIg8yNzNFMDk1RjVB\n" +
886
+ # "BzsGVDobQHVuY29udmVydGFibGVfdG9fYnNvblRJIgl0YWdzBjsGVFsASSIL\n" +
887
+ # "YWN0aXZlBjsGVFRJIhhzdWJzY3JpYmVkX3VzZXJfaWRzBjsGVFsASSIMZGV0\n" +
888
+ # "YWlscwY7BlRDOwB7BkkiB2VuBjsGVEM7AHsASSIMZmlsdGVycwY7BlRDOwB7\n" +
889
+ # "BkkiB2VuBjsGVEM7AHsASSINdGVtcGxhdGUGOwZUSSIMZ2VuZXJpYwY7BlRJ\n" +
890
+ # "IhBwdXJjaGFzYWJsZQY7BlRUSSIJbmFtZQY7BlRDOwB7BkkiB2VuBjsGVEki\n" +
891
+ # "FEVzY2FwZSB0aGUgUm9vbQY7BlRJIgxkaWdpdGFsBjsGVEZJIglzbHVnBjsG\n" +
892
+ # "VEkiFGVzY2FwZS10aGUtcm9vbQY7BlRJIg91cGRhdGVkX2F0BjsGVEl1OglU\n" +
893
+ # "aW1lDfJeHcBolXe5BjoJem9uZUkiCFVUQwY7BkZJIg9jcmVhdGVkX2F0BjsG\n" +
894
+ # "VEl1OwgN8l4dwGiVd7kGOwlJIghVVEMGOwZG\n"},
895
+ # "sort"=>[6.044283, 1503513983497]}]},
896
+ # "aggregations"=> # ...}
897
+ ```
898
+
899
+ The `#results` method returns the "loaded" results, which de-serializes each result into a Mogoid model. These results are suitable for display in the UI.
900
+
901
+ ```ruby
902
+ product_admin_search_query.results.class
903
+ # => Workarea::PagedArray
904
+
905
+ product_admin_search_query.results.to_a.map(&:class)
906
+ # => [Workarea::Catalog::Product]
907
+
908
+ product_admin_search_query.results.first.id
909
+ # => "273E095F5A"
910
+
911
+ product_admin_search_query.results.first.name
912
+ # => "Escape the Room"
913
+ ```
914
+
915
+ ### Product Results
916
+
917
+ Search queries that return products, such as `ProductSearch`, `CategoryBrowse`, and `RelatedProducts` include the `LoadProductResults` module, which changes the behavior of `results`.
918
+
919
+ For each result, these queries return a hash of objects rather than a single object.
920
+
921
+ ```ruby
922
+ product_search = Workarea::Search::ProductSearch.new(q: 'marble')
923
+
924
+ product_search.results.class
925
+ # => Workarea::PagedArray
926
+
927
+ product_search.results.to_a.map(&:class)
928
+ # => [Hash, Hash, Hash, Hash, Hash, Hash, Hash, Hash, Hash]
929
+ ```
930
+
931
+ The following examples show the keys in this hash and the type of each value.
932
+
933
+ ```ruby
934
+ product_search.results.first.keys
935
+ # => [:id, :catalog_id, :model, :option, :pricing, :inventory]
936
+
937
+ pp Hash[product_search.results.first.map { |k,v| [k, v.class] }]
938
+ # {:id=>String,
939
+ # :catalog_id=>String,
940
+ # :model=>Workarea::Catalog::Product,
941
+ # :option=>NilClass,
942
+ # :pricing=>Workarea::Pricing::Collection,
943
+ # :inventory=>Workarea::Inventory::Collection}
944
+
945
+ product_search.results.first[:catalog_id]
946
+ # => "29C9ECAAF2"
947
+
948
+ product_search.results.first[:model].name
949
+ # => "Practical Marble Clock"
950
+ ```
951
+
952
+ ### Composing the Search Request Body
953
+
954
+ As mentioned above, search queries perform an Elasticsearch request body search. These search requests use a request body constructed from the [Elasticsearch query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/query-dsl.html).
955
+
956
+ Search queries provide a Ruby interface for composing these request bodies. The `body` method is responsible for returning a hash which represents the request body. Many other methods are potentially used to compose this final hash. Below is the default implementation of `Workarea::Search::Query#body`.
957
+
958
+ ```ruby
959
+ module Workarea
960
+ module Search
961
+ module Query
962
+ # ...
963
+
964
+ def body
965
+ {
966
+ query: query,
967
+ post_filter: post_filter,
968
+ aggs: aggregations
969
+ }
970
+ .merge(additional_options)
971
+ .delete_if { |_, v| v.blank? }
972
+ end
973
+
974
+ # ...
975
+ end
976
+ end
977
+ end
978
+ ```
979
+
980
+ Each search query extends or implements the methods necessary to produce the desired request body. As you can see above, the methods `query`, `post_filter`, and `aggregations` contribute directly to the body. The `additional_options` method, calls a method for each search query option included in `Workarea.config.search_query_options`, allowing for configuration of the query builder.
981
+
982
+ ```ruby
983
+ Workarea.config.search_query_options
984
+ # => ["sort", "size", "from", "suggest"]
985
+ ```
986
+
987
+ Many search queries include some of the following modules, which help to build up the desired request body.
988
+
989
+ - `Workarea::Search::CategorizationFiltering`
990
+ - `Workarea::Search::Facets`
991
+ - `Workarea::Search::Pagination`
992
+ - `Workarea::Search::LoadProductResults`
993
+ - `Workarea::Search::ProductDisplayRules`
994
+ - `Workarea::Search::QuerySuggestions`
995
+ - `Workarea::Search::ProductRules`
996
+ - `Workarea::Search::AdminIndexSearch`
997
+ - `Workarea::Search::AdminSorting`
998
+
999
+ ## Notes
1000
+
1001
+ [1] Documents of type _category_ are stored in the same indexes as documents of type _storefront_.
1002
+
1003
+ [2] The _storefront_ key declares mappings for the _storefront_ **and** _category_ types, since both document types are stored in the same indexes.
1004
+
1005
+ [3] `Workarea::Search::Help` has no descendants and is used directly to save and destroy documents.