workarea-core 3.4.44 → 3.5.0.beta.1

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 (427) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +52 -0
  3. data/app/assets/javascripts/workarea/core/modules/duplicate_id.js +1 -5
  4. data/app/assets/javascripts/workarea/core/modules/style_guide_autocomplete_fields.js +9 -42
  5. data/app/assets/javascripts/workarea/core/modules/url.js +14 -0
  6. data/app/controllers/workarea/application_controller.rb +5 -1
  7. data/app/controllers/workarea/authentication.rb +15 -26
  8. data/app/controllers/workarea/authorization.rb +3 -3
  9. data/app/controllers/workarea/current_referrer.rb +1 -2
  10. data/app/controllers/workarea/current_release.rb +1 -1
  11. data/app/controllers/workarea/current_segments.rb +52 -0
  12. data/app/controllers/workarea/current_tracking.rb +45 -0
  13. data/app/controllers/workarea/http_caching.rb +30 -0
  14. data/app/controllers/workarea/impersonation.rb +18 -44
  15. data/app/helpers/workarea/schema_org_helper.rb +21 -0
  16. data/app/mailers/workarea/application_mailer.rb +2 -4
  17. data/app/middleware/workarea/application_middleware.rb +44 -0
  18. data/app/middleware/workarea/query_cache_middleware.rb +55 -0
  19. data/app/middleware/workarea/release_server_middleware.rb +1 -1
  20. data/app/models/workarea/application_document.rb +1 -1
  21. data/app/models/workarea/bulk_action/product_edit.rb +6 -6
  22. data/app/models/workarea/calculate_percent_change.rb +1 -1
  23. data/app/models/workarea/catalog/category.rb +0 -2
  24. data/app/models/workarea/catalog/product.rb +19 -2
  25. data/app/models/workarea/checkout.rb +18 -6
  26. data/app/models/workarea/checkout/collect_payment.rb +10 -1
  27. data/app/models/workarea/checkout/fraud/analyzer.rb +44 -0
  28. data/app/models/workarea/checkout/fraud/no_decision_analyzer.rb +14 -0
  29. data/app/models/workarea/checkout/fraud/test_analyzer.rb +26 -0
  30. data/app/models/workarea/configuration/admin.rb +25 -0
  31. data/app/models/workarea/configuration/params.rb +45 -0
  32. data/app/models/workarea/content.rb +12 -82
  33. data/app/models/workarea/content/asset.rb +5 -18
  34. data/app/models/workarea/content/block.rb +8 -2
  35. data/app/models/workarea/content/block_draft.rb +7 -2
  36. data/app/models/workarea/content/block_type.rb +2 -2
  37. data/app/models/workarea/content/block_type_definition.rb +4 -0
  38. data/app/models/workarea/content/preset.rb +1 -1
  39. data/app/models/workarea/data_file/csv.rb +6 -8
  40. data/app/models/workarea/data_file/import.rb +9 -1
  41. data/app/models/workarea/data_file/json.rb +1 -20
  42. data/app/models/workarea/data_file/operation.rb +0 -4
  43. data/app/models/workarea/data_file/tax_rates.rb +3 -0
  44. data/app/models/workarea/featured_products.rb +8 -0
  45. data/app/models/workarea/fulfillment/policies/base.rb +21 -0
  46. data/app/models/workarea/fulfillment/policies/download.rb +21 -0
  47. data/app/models/workarea/fulfillment/policies/ignore.rb +11 -0
  48. data/app/models/workarea/fulfillment/policies/ship.rb +15 -0
  49. data/app/models/workarea/fulfillment/sku.rb +72 -0
  50. data/app/models/workarea/fulfillment/token.rb +36 -0
  51. data/app/models/workarea/inquiry.rb +1 -2
  52. data/app/models/workarea/insights/customer_acquisition.rb +5 -9
  53. data/app/models/workarea/insights/trending_products.rb +2 -2
  54. data/app/models/workarea/insights/trending_searches.rb +2 -2
  55. data/app/models/workarea/inventory/collection.rb +30 -0
  56. data/app/models/workarea/inventory/collection_status.rb +40 -0
  57. data/app/models/workarea/metrics/affinity.rb +33 -0
  58. data/app/models/workarea/metrics/country_by_day.rb +4 -0
  59. data/app/models/workarea/metrics/product_by_day.rb +3 -0
  60. data/app/models/workarea/metrics/product_by_week.rb +2 -0
  61. data/app/models/workarea/metrics/product_for_last_week.rb +8 -12
  62. data/app/models/workarea/metrics/sales_by_day.rb +5 -0
  63. data/app/models/workarea/metrics/search_by_day.rb +1 -3
  64. data/app/models/workarea/metrics/search_for_last_week.rb +5 -11
  65. data/app/models/workarea/metrics/segment_by_day.rb +31 -0
  66. data/app/models/workarea/metrics/sku_by_day.rb +3 -0
  67. data/app/models/workarea/metrics/tender_by_day.rb +11 -0
  68. data/app/models/workarea/metrics/update_user_aggregations.rb +77 -0
  69. data/app/models/workarea/metrics/user.rb +73 -87
  70. data/app/models/workarea/navigation/breadcrumbs.rb +10 -1
  71. data/app/models/workarea/order.rb +47 -4
  72. data/app/models/workarea/order/fraud_decision.rb +18 -0
  73. data/app/models/workarea/order/item.rb +5 -0
  74. data/app/models/workarea/order/queries.rb +1 -0
  75. data/app/models/workarea/order/status.rb +10 -1
  76. data/app/models/workarea/payment.rb +7 -2
  77. data/app/models/workarea/payment/credit_card.rb +12 -0
  78. data/app/models/workarea/pricing/cache_key.rb +9 -3
  79. data/app/models/workarea/pricing/calculator.rb +5 -1
  80. data/app/models/workarea/pricing/calculators/tax_calculator.rb +32 -5
  81. data/app/models/workarea/pricing/discount.rb +2 -3
  82. data/app/models/workarea/pricing/discount/application_group.rb +8 -3
  83. data/app/models/workarea/pricing/discount/free_gift.rb +2 -1
  84. data/app/models/workarea/pricing/discount/redemption.rb +1 -1
  85. data/app/models/workarea/pricing/item_tax_applier.rb +41 -0
  86. data/app/models/workarea/pricing/price.rb +1 -0
  87. data/app/models/workarea/pricing/request.rb +11 -0
  88. data/app/models/workarea/pricing/sku.rb +1 -0
  89. data/app/models/workarea/pricing/tax_application.rb +21 -0
  90. data/app/models/workarea/pricing/tax_applier.rb +12 -15
  91. data/app/models/workarea/pricing/unsupported_segmentation.rb +22 -0
  92. data/app/models/workarea/product_rule.rb +5 -0
  93. data/app/models/workarea/releasable.rb +83 -89
  94. data/app/models/workarea/release.rb +72 -99
  95. data/app/models/workarea/release/activation.rb +45 -0
  96. data/app/models/workarea/release/changes.rb +45 -0
  97. data/app/models/workarea/release/changeset.rb +42 -52
  98. data/app/models/workarea/release/preview.rb +28 -0
  99. data/app/models/workarea/release/status.rb +2 -24
  100. data/app/models/workarea/release_session.rb +11 -11
  101. data/app/models/workarea/search/admin.rb +17 -39
  102. data/app/models/workarea/search/admin/catalog_product.rb +1 -2
  103. data/app/models/workarea/search/admin/fulfillment_sku.rb +27 -0
  104. data/app/models/workarea/search/admin/order.rb +1 -1
  105. data/app/models/workarea/search/admin/pricing_discount.rb +1 -1
  106. data/app/models/workarea/search/admin/release.rb +1 -4
  107. data/app/models/workarea/search/admin/segment.rb +19 -0
  108. data/app/models/workarea/search/order_text.rb +23 -6
  109. data/app/models/workarea/search/storefront.rb +33 -29
  110. data/app/models/workarea/search/storefront/category_query.rb +152 -0
  111. data/app/models/workarea/search/storefront/product/categories.rb +11 -45
  112. data/app/models/workarea/segment.rb +54 -0
  113. data/app/models/workarea/segment/first_time_customer.rb +8 -0
  114. data/app/models/workarea/segment/first_time_visitor.rb +12 -0
  115. data/app/models/workarea/segment/life_cycle.rb +44 -0
  116. data/app/models/workarea/segment/loyal_customer.rb +12 -0
  117. data/app/models/workarea/segment/returning_customer.rb +14 -0
  118. data/app/models/workarea/segment/returning_visitor.rb +12 -0
  119. data/app/models/workarea/segment/rules/base.rb +16 -0
  120. data/app/models/workarea/segment/rules/browser_info.rb +29 -0
  121. data/app/models/workarea/segment/rules/geolocation.rb +29 -0
  122. data/app/models/workarea/segment/rules/geolocation_option.rb +53 -0
  123. data/app/models/workarea/segment/rules/last_order.rb +14 -0
  124. data/app/models/workarea/segment/rules/logged_in.rb +13 -0
  125. data/app/models/workarea/segment/rules/orders.rb +17 -0
  126. data/app/models/workarea/segment/rules/revenue.rb +17 -0
  127. data/app/models/workarea/segment/rules/sessions.rb +17 -0
  128. data/app/models/workarea/segment/rules/tags.rb +14 -0
  129. data/app/models/workarea/segment/rules/traffic_referrer.rb +18 -0
  130. data/app/models/workarea/segmentable.rb +66 -0
  131. data/app/models/workarea/sort.rb +14 -2
  132. data/app/models/workarea/tax/rate.rb +35 -1
  133. data/app/models/workarea/user/authorization.rb +19 -13
  134. data/app/models/workarea/user/login.rb +4 -0
  135. data/app/models/workarea/user/password_reset.rb +1 -3
  136. data/app/queries/workarea/categorization.rb +3 -3
  137. data/app/queries/workarea/featured_categorization.rb +53 -0
  138. data/app/queries/workarea/order_cancellation_metrics.rb +155 -0
  139. data/app/queries/workarea/order_item_details.rb +16 -3
  140. data/app/queries/workarea/order_metrics.rb +32 -3
  141. data/app/queries/workarea/product_primary_navigation.rb +13 -2
  142. data/app/queries/workarea/product_releases.rb +38 -0
  143. data/app/queries/workarea/recommendation/user_activity_based.rb +14 -6
  144. data/app/queries/workarea/reports/average_order_value.rb +1 -1
  145. data/app/queries/workarea/reports/customers.rb +14 -11
  146. data/app/queries/workarea/reports/first_time_vs_returning_sales.rb +1 -1
  147. data/app/queries/workarea/reports/group_by_time.rb +13 -17
  148. data/app/queries/workarea/reports/sales_by_category.rb +1 -1
  149. data/app/queries/workarea/reports/sales_by_country.rb +10 -3
  150. data/app/queries/workarea/reports/sales_by_discount.rb +1 -1
  151. data/app/queries/workarea/reports/sales_by_menu.rb +1 -1
  152. data/app/queries/workarea/reports/sales_by_product.rb +39 -4
  153. data/app/queries/workarea/reports/sales_by_sku.rb +39 -4
  154. data/app/queries/workarea/reports/sales_by_tender.rb +42 -0
  155. data/app/queries/workarea/reports/sales_by_traffic_referrer.rb +1 -1
  156. data/app/queries/workarea/reports/sales_over_time.rb +10 -3
  157. data/app/queries/workarea/reports/searches.rb +1 -1
  158. data/app/queries/workarea/reports/searches_over_time.rb +1 -1
  159. data/app/queries/workarea/reports/searches_without_results_over_time.rb +1 -1
  160. data/app/queries/workarea/search/admin_fulfillment_skus.rb +20 -0
  161. data/app/queries/workarea/search/admin_releases.rb +1 -4
  162. data/app/queries/workarea/search/admin_search.rb +0 -4
  163. data/app/queries/workarea/search/admin_segments.rb +16 -0
  164. data/app/queries/workarea/search/admin_sorting.rb +1 -1
  165. data/app/queries/workarea/search/pagination.rb +1 -4
  166. data/app/queries/workarea/search/product_display_rules.rb +40 -8
  167. data/app/queries/workarea/search/product_entries.rb +15 -1
  168. data/app/queries/workarea/search/product_search.rb +4 -1
  169. data/app/queries/workarea/search/storefront_search.rb +5 -4
  170. data/app/queries/workarea/search/storefront_search/exact_matches.rb +1 -1
  171. data/app/queries/workarea/search/storefront_search/response.rb +6 -4
  172. data/app/seeds/workarea/assets_seeds.rb +2 -1
  173. data/app/seeds/workarea/insights_seeds.rb +0 -23
  174. data/app/seeds/workarea/orders_seeds.rb +7 -6
  175. data/app/seeds/workarea/products_seeds.rb +2 -0
  176. data/app/seeds/workarea/segments_seeds.rb +8 -0
  177. data/app/seeds/workarea/tax_seeds.rb +3 -3
  178. data/app/services/workarea/cancel_order.rb +8 -3
  179. data/app/services/workarea/copy_order.rb +6 -0
  180. data/app/services/workarea/create_fulfillment.rb +5 -3
  181. data/app/services/workarea/direct_upload.rb +11 -19
  182. data/app/services/workarea/hash_update.rb +1 -18
  183. data/app/services/workarea/packaging.rb +1 -1
  184. data/app/services/workarea/save_publishing.rb +5 -1
  185. data/app/workers/sidekiq/callbacks.rb +4 -52
  186. data/app/workers/sidekiq/callbacks_worker.rb +4 -6
  187. data/app/workers/workarea/bulk_index_admin.rb +2 -2
  188. data/app/workers/workarea/bulk_index_products.rb +6 -5
  189. data/app/workers/workarea/bulk_index_searches.rb +5 -5
  190. data/app/workers/workarea/deactivate_stale_discounts.rb +3 -3
  191. data/app/workers/workarea/generate_insights.rb +1 -1
  192. data/app/workers/workarea/index_admin_search.rb +1 -0
  193. data/app/workers/workarea/index_categorization.rb +4 -7
  194. data/app/workers/workarea/index_category.rb +2 -1
  195. data/app/workers/workarea/index_category_changes.rb +11 -6
  196. data/app/workers/workarea/index_fulfillment_changes.rb +2 -1
  197. data/app/workers/workarea/index_help.rb +2 -1
  198. data/app/workers/workarea/index_page.rb +2 -1
  199. data/app/workers/workarea/index_payment_transactions.rb +2 -1
  200. data/app/workers/workarea/index_product.rb +4 -4
  201. data/app/workers/workarea/index_product_children.rb +4 -3
  202. data/app/workers/workarea/index_product_rule.rb +3 -2
  203. data/app/workers/workarea/index_search_customizations.rb +3 -2
  204. data/app/workers/workarea/index_skus.rb +5 -6
  205. data/app/workers/workarea/keep_product_index_fresh.rb +3 -5
  206. data/app/workers/workarea/process_import.rb +3 -3
  207. data/app/workers/workarea/process_search_recommendations.rb +5 -9
  208. data/app/workers/workarea/publish_release.rb +1 -0
  209. data/app/workers/workarea/save_metrics.rb +116 -0
  210. data/app/workers/workarea/save_order_cancellation_metrics.rb +35 -0
  211. data/app/workers/workarea/save_order_metrics.rb +10 -74
  212. data/app/workers/workarea/status_reporter.rb +2 -4
  213. data/app/workers/workarea/synchronize_user_metrics.rb +17 -0
  214. data/app/workers/workarea/update_payment_profile_email.rb +22 -0
  215. data/app/workers/workarea/verify_scheduled_releases.rb +4 -11
  216. data/config/initializers/00_configuration.rb +185 -0
  217. data/config/initializers/02_assets.rb +1 -0
  218. data/config/initializers/05_scheduled_jobs.rb +0 -7
  219. data/config/initializers/10_rack_middleware.rb +2 -5
  220. data/config/initializers/11_payment.rb +4 -0
  221. data/config/initializers/13_rack_attack.rb +2 -4
  222. data/config/initializers/14_content_block_types.rb +16 -16
  223. data/config/initializers/15_endpoint_monitoring.rb +3 -6
  224. data/config/initializers/{21_premailer.rb → 24_premailer.rb} +1 -0
  225. data/config/initializers/{20_i18n_js.rb → 25_i18n_js.rb} +0 -0
  226. data/config/locales/en.yml +13 -24
  227. data/lib/generators/workarea/content_block_type/templates/initializer.rb +1 -1
  228. data/lib/generators/workarea/install/USAGE +0 -1
  229. data/lib/generators/workarea/install/install_generator.rb +0 -21
  230. data/lib/generators/workarea/install/templates/initializer.rb.erb +1 -1
  231. data/lib/tasks/insights.rake +1 -0
  232. data/lib/tasks/migrate.rake +106 -0
  233. data/lib/tasks/search.rake +17 -19
  234. data/lib/tasks/tests.rake +1 -1
  235. data/lib/workarea.rb +102 -3
  236. data/lib/workarea/cache.rb +6 -35
  237. data/lib/workarea/changelog.rake +2 -2
  238. data/lib/workarea/configuration.rb +121 -148
  239. data/lib/workarea/configuration/administrable/definition.rb +42 -0
  240. data/lib/workarea/configuration/administrable/field.rb +79 -0
  241. data/lib/workarea/configuration/administrable/fieldset.rb +44 -0
  242. data/lib/workarea/configuration/administrable_options.rb +24 -0
  243. data/lib/workarea/configuration/cache_store.rb +3 -1
  244. data/lib/workarea/configuration/content_blocks.rb +17 -0
  245. data/lib/workarea/configuration/mongoid.rb +1 -4
  246. data/lib/workarea/configuration/session.rb +36 -0
  247. data/lib/workarea/configuration/sidekiq.rb +3 -1
  248. data/lib/workarea/core.rb +15 -11
  249. data/lib/workarea/core/engine.rb +3 -58
  250. data/lib/workarea/elasticsearch/document.rb +8 -15
  251. data/lib/workarea/ext/freedom_patches/action_view_cache_helper.rb +4 -6
  252. data/lib/workarea/ext/freedom_patches/active_support_duration.rb +14 -0
  253. data/lib/workarea/ext/freedom_patches/money.rb +11 -0
  254. data/lib/workarea/ext/freedom_patches/premailer.rb +6 -0
  255. data/lib/workarea/ext/freedom_patches/string.rb +19 -4
  256. data/lib/workarea/ext/mongoid/find_ordered.rb +2 -2
  257. data/lib/workarea/ext/mongoid/lookup_hash.rb +30 -0
  258. data/lib/workarea/geolocation.rb +24 -6
  259. data/lib/workarea/routes_constraints/super_admin.rb +1 -1
  260. data/lib/workarea/version.rb +3 -4
  261. data/lib/workarea/visit.rb +92 -0
  262. data/lib/workarea/warnings.rb +95 -0
  263. data/test/dummy/config/initializers/session_store.rb +1 -1
  264. data/test/fixtures/tax_rates.csv +3 -3
  265. data/test/generators/workarea/content_block_type_generator_test.rb +1 -1
  266. data/test/generators/workarea/install_generator_test.rb +0 -13
  267. data/test/integration/workarea/authentication_test.rb +27 -4
  268. data/test/integration/workarea/authorization_test.rb +5 -0
  269. data/test/integration/workarea/cache_varies_integration_test.rb +21 -23
  270. data/test/integration/workarea/monitoring_integration_test.rb +5 -10
  271. data/test/javascripts/duplicate_id_spec.js +1 -7
  272. data/test/javascripts/fixtures/{duplicate_id_fail.html.haml → duplicate_id.html.haml} +0 -1
  273. data/test/javascripts/form_submitting_controls_spec.js +0 -1
  274. data/test/lib/workarea/asset_endpoints/favicons_test.rb +11 -13
  275. data/test/lib/workarea/configuration/administrable/definition_test.rb +71 -0
  276. data/test/lib/workarea/configuration/administrable/field_test.rb +128 -0
  277. data/test/lib/workarea/configuration/administrable/fieldset_test.rb +78 -0
  278. data/test/lib/workarea/configuration/configuration_test.rb +20 -0
  279. data/test/lib/workarea/elasticsearch/document_test.rb +0 -20
  280. data/test/lib/workarea/elasticsearch/query_cache_test.rb +7 -7
  281. data/test/lib/workarea/ext/freedom_patches/money_test.rb +11 -0
  282. data/test/lib/workarea/ext/mongoid/lookup_hash_test.rb +86 -0
  283. data/test/lib/workarea/geolocation_test.rb +20 -0
  284. data/test/mailers/workarea/application_mailer_test.rb +1 -11
  285. data/test/middleware/workarea/query_cache_middleware_test.rb +55 -0
  286. data/test/models/workarea/checkout/collect_payment_test.rb +14 -6
  287. data/test/models/workarea/checkout_test.rb +31 -57
  288. data/test/models/workarea/configuration/params_test.rb +37 -0
  289. data/test/models/workarea/data_file/csv_test.rb +8 -73
  290. data/test/models/workarea/data_file/import_test.rb +31 -0
  291. data/test/models/workarea/data_file/json_test.rb +6 -48
  292. data/test/models/workarea/data_file/tax_test.rb +3 -0
  293. data/test/models/workarea/fulfillment/policies/download_test.rb +39 -0
  294. data/test/models/workarea/fulfillment/sku_test.rb +54 -0
  295. data/test/models/workarea/insights/best_customers_test.rb +5 -2
  296. data/test/models/workarea/insights/best_full_price_customers_test.rb +5 -2
  297. data/test/models/workarea/insights/customer_acquisition_test.rb +1 -1
  298. data/test/models/workarea/insights/customers_at_risk_test.rb +1 -1
  299. data/test/models/workarea/insights/low_aov_customers_test.rb +1 -1
  300. data/test/models/workarea/inventory/collection_test.rb +75 -0
  301. data/test/models/workarea/metrics/affinity_test.rb +85 -0
  302. data/test/models/workarea/metrics/product_by_week_test.rb +27 -31
  303. data/test/models/workarea/metrics/search_by_day_test.rb +3 -0
  304. data/test/models/workarea/metrics/search_by_week_test.rb +27 -31
  305. data/test/models/workarea/metrics/update_user_aggregations_test.rb +62 -0
  306. data/test/models/workarea/metrics/user_test.rb +146 -63
  307. data/test/models/workarea/navigation/breadcrumbs_test.rb +12 -2
  308. data/test/models/workarea/order/queries_test.rb +2 -0
  309. data/test/models/workarea/order_test.rb +44 -0
  310. data/test/models/workarea/payment/address_test.rb +109 -111
  311. data/test/models/workarea/payment/credit_card_integration_test.rb +0 -52
  312. data/test/models/workarea/payment/credit_card_test.rb +5 -0
  313. data/test/models/workarea/pricing/cache_key_test.rb +7 -1
  314. data/test/models/workarea/pricing/calculators/tax_calculator_test.rb +41 -1
  315. data/test/models/workarea/pricing/discount/collection_test.rb +27 -29
  316. data/test/models/workarea/pricing/discount_test.rb +22 -24
  317. data/test/models/workarea/pricing/item_tax_applier_test.rb +83 -0
  318. data/test/models/workarea/pricing/price_test.rb +9 -0
  319. data/test/models/workarea/pricing/request_test.rb +13 -0
  320. data/test/models/workarea/pricing/sku_test.rb +9 -0
  321. data/test/models/workarea/releasable_active_test.rb +113 -0
  322. data/test/models/workarea/releasable_test.rb +69 -120
  323. data/test/models/workarea/release/changeset_test.rb +41 -0
  324. data/test/models/workarea/release_jobs_test.rb +52 -0
  325. data/test/models/workarea/release_previewing_test.rb +43 -0
  326. data/test/models/workarea/release_test.rb +59 -153
  327. data/test/models/workarea/search/admin/content_asset_test.rb +26 -28
  328. data/test/models/workarea/search/order_text_test.rb +35 -0
  329. data/test/models/workarea/search/storefront/category_query_test.rb +81 -0
  330. data/test/models/workarea/search/storefront/product/categories_test.rb +10 -70
  331. data/test/models/workarea/search/storefront/product_test.rb +10 -11
  332. data/test/models/workarea/search/storefront_test.rb +1 -13
  333. data/test/models/workarea/segment/life_cycle_test.rb +24 -0
  334. data/test/models/workarea/segment/rules/browser_info_test.rb +33 -0
  335. data/test/models/workarea/segment/rules/geolocation_option_test.rb +16 -0
  336. data/test/models/workarea/segment/rules/geolocation_test.rb +49 -0
  337. data/test/models/workarea/segment/rules/last_order_test.rb +32 -0
  338. data/test/models/workarea/segment/rules/logged_in_test.rb +25 -0
  339. data/test/models/workarea/segment/rules/orders_test.rb +59 -0
  340. data/test/models/workarea/segment/rules/revenue_test.rb +59 -0
  341. data/test/models/workarea/segment/rules/sessions_test.rb +26 -0
  342. data/test/models/workarea/segment/rules/tags_test.rb +27 -0
  343. data/test/models/workarea/segment/rules/traffic_referrer_test.rb +31 -0
  344. data/test/models/workarea/segment_test.rb +80 -0
  345. data/test/models/workarea/segmentable_test.rb +74 -0
  346. data/test/models/workarea/shipping/address_test.rb +109 -111
  347. data/test/models/workarea/shipping/sku_test.rb +8 -12
  348. data/test/models/workarea/user/password_reset_test.rb +4 -12
  349. data/test/models/workarea/user_test.rb +13 -10
  350. data/test/queries/workarea/admin_search_query_wrapper_test.rb +21 -25
  351. data/test/queries/workarea/categorization_test.rb +49 -0
  352. data/test/queries/workarea/featured_categorization_test.rb +66 -0
  353. data/test/queries/workarea/order_cancellation_metrics_test.rb +203 -0
  354. data/test/queries/workarea/order_item_details_test.rb +17 -0
  355. data/test/queries/workarea/order_metrics_test.rb +12 -0
  356. data/test/queries/workarea/product_releases_test.rb +56 -0
  357. data/test/queries/workarea/recommendation/searches_test.rb +3 -3
  358. data/test/queries/workarea/recommendation/user_activity_based_test.rb +4 -4
  359. data/test/queries/workarea/reports/customers_test.rb +12 -8
  360. data/test/queries/workarea/reports/report_test.rb +4 -6
  361. data/test/queries/workarea/reports/sales_by_country_test.rb +18 -3
  362. data/test/queries/workarea/reports/sales_by_product_test.rb +28 -8
  363. data/test/queries/workarea/reports/sales_by_sku_test.rb +28 -8
  364. data/test/queries/workarea/reports/sales_by_tender_test.rb +100 -0
  365. data/test/queries/workarea/reports/sales_by_traffic_referrer_test.rb +6 -6
  366. data/test/queries/workarea/search/admin_releases_test.rb +1 -26
  367. data/test/queries/workarea/search/admin_search_test.rb +0 -10
  368. data/test/queries/workarea/search/admin_users_test.rb +2 -2
  369. data/test/queries/workarea/search/category_browse_test.rb +53 -0
  370. data/test/queries/workarea/search/facet_sorting/size_test.rb +11 -13
  371. data/test/queries/workarea/search/pagination_test.rb +9 -26
  372. data/test/queries/workarea/search/product_entries_test.rb +27 -0
  373. data/test/queries/workarea/search/product_search_test.rb +0 -16
  374. data/test/queries/workarea/search/storefront_search/response_test.rb +26 -0
  375. data/test/queries/workarea/search/storefront_search_test.rb +34 -36
  376. data/test/services/workarea/cancel_order_test.rb +27 -13
  377. data/test/services/workarea/copy_order_test.rb +17 -0
  378. data/test/services/workarea/direct_upload_test.rb +1 -46
  379. data/test/services/workarea/export_report_test.rb +41 -45
  380. data/test/services/workarea/hash_update_test.rb +12 -12
  381. data/test/services/workarea/packaging_test.rb +9 -11
  382. data/test/services/workarea/save_publishing_test.rb +23 -0
  383. data/test/workers/sidekiq/callbacks_test.rb +0 -22
  384. data/test/workers/workarea/deactivate_stale_discounts_test.rb +2 -2
  385. data/test/workers/workarea/index_category_changes_test.rb +5 -7
  386. data/test/workers/workarea/keep_product_index_fresh_test.rb +1 -1
  387. data/test/workers/workarea/process_import_test.rb +0 -6
  388. data/test/workers/workarea/process_search_recommendations_test.rb +4 -5
  389. data/test/workers/workarea/status_reporter_test.rb +2 -1
  390. data/test/workers/workarea/synchronize_user_metrics_test.rb +57 -0
  391. data/test/workers/workarea/update_payment_profile_email_test.rb +27 -0
  392. data/test/workers/workarea/verify_scheduled_releases_test.rb +0 -26
  393. data/workarea-core.gemspec +12 -10
  394. metadata +188 -78
  395. data/app/assets/javascripts/workarea/core/templates/ui_menu_item.jst.ejs +0 -10
  396. data/app/controllers/workarea/admin_guest_browsing.rb +0 -54
  397. data/app/middleware/workarea/rack_cache_config_middleware.rb +0 -19
  398. data/app/queries/workarea/search/search_suggestions.rb +0 -43
  399. data/app/workers/workarea/bust_discount_cache.rb +0 -16
  400. data/app/workers/workarea/clean_user_activity.rb +0 -11
  401. data/app/workers/workarea/undo_release.rb +0 -15
  402. data/app/workers/workarea/update_email.rb +0 -33
  403. data/lib/generators/workarea/docker/USAGE +0 -16
  404. data/lib/generators/workarea/docker/docker_generator.rb +0 -61
  405. data/lib/generators/workarea/docker/templates/.env.erb +0 -6
  406. data/lib/generators/workarea/docker/templates/Dockerfile.erb +0 -48
  407. data/lib/generators/workarea/docker/templates/docker-compose.yml.erb +0 -72
  408. data/lib/generators/workarea/docker/templates/docker-entrypoint.sh.erb +0 -6
  409. data/lib/generators/workarea/docker/templates/docker-sync.yml.erb +0 -8
  410. data/lib/generators/workarea/docker/templates/docker-wait.sh +0 -11
  411. data/lib/generators/workarea/docker/templates/docker.env +0 -16
  412. data/lib/generators/workarea/docker/templates/docker_init.rb.erb +0 -29
  413. data/lib/workarea/configuration/headless_chrome.rb +0 -42
  414. data/lib/workarea/configuration/puma.rb +0 -37
  415. data/lib/workarea/ext/freedom_patches/i18n_js.rb +0 -27
  416. data/lib/workarea/ext/freedom_patches/mongoid_localized_defaults.rb +0 -25
  417. data/lib/workarea/ext/jbuilder/jbuilder_cache.rb +0 -29
  418. data/lib/workarea/ext/referer_parser/parser.decorator +0 -43
  419. data/lib/workarea/queues_pauser.rb +0 -26
  420. data/lib/workarea/robots.rb +0 -9
  421. data/test/generators/workarea/docker_generator_test.rb +0 -156
  422. data/test/javascripts/fixtures/duplicate_id_pass.html.haml +0 -2
  423. data/test/lib/workarea/ext/freedom_patches/mongoid_localized_defaults_test.rb +0 -25
  424. data/test/lib/workarea/ext/referer_parser/parser_test.rb +0 -20
  425. data/test/queries/workarea/search/search_suggestions_test.rb +0 -29
  426. data/test/workers/workarea/undo_release_test.rb +0 -23
  427. data/test/workers/workarea/update_email_test.rb +0 -39
@@ -0,0 +1,22 @@
1
+ module Workarea
2
+ module Pricing
3
+ module UnsupportedSegmentation
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ validate :unsupported_segmentation
8
+ end
9
+
10
+ private
11
+
12
+ def unsupported_segmentation
13
+ if active_segment_ids.present?
14
+ errors.add(
15
+ :active_segment_ids,
16
+ I18n.t('workarea.errors.messages.unsupported_segmentation')
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,6 +1,7 @@
1
1
  module Workarea
2
2
  class ProductRule
3
3
  include ApplicationDocument
4
+ include Releasable
4
5
 
5
6
  OPERATORS = %w(
6
7
  greater_than
@@ -22,6 +23,10 @@ module Workarea
22
23
  validates :name, presence: true
23
24
  validates :operator, presence: true
24
25
 
26
+ def self.usable
27
+ scoped.select(&:active?).select(&:valid?)
28
+ end
29
+
25
30
  # The identifier for which admin partials to render for this rule.
26
31
  #
27
32
  # @return [Symbol]
@@ -2,9 +2,12 @@ module Workarea
2
2
  module Releasable
3
3
  extend ActiveSupport::Concern
4
4
  include Mongoid::DocumentPath
5
+ include Release::Activation
6
+ include Segmentable
5
7
 
6
8
  included do
7
9
  field :active, type: Boolean, default: true, localize: Workarea.config.localized_active_fields
10
+ attr_accessor :release_id
8
11
 
9
12
  has_many :changesets,
10
13
  class_name: 'Workarea::Release::Changeset',
@@ -12,11 +15,8 @@ module Workarea
12
15
 
13
16
  validate :slug_unchanged, on: :update
14
17
 
15
- scope :active, -> { where(active: true) }
16
- scope :inactive, -> { where(active: false) }
17
-
18
- around_create :save_activate_with
19
- before_update :save_release_changes
18
+ define_model_callbacks :save_release_changes
19
+ before_update :handle_release_changes
20
20
  after_find :load_release_changes
21
21
  after_destroy :destroy_embedded_changesets
22
22
 
@@ -25,8 +25,22 @@ module Workarea
25
25
  else
26
26
  index(active: 1)
27
27
  end
28
+ end
29
+
30
+ def changesets_with_children
31
+ criteria = Release::Changeset.any_of(
32
+ { releasable_type: self.class.name, releasable_id: id }
33
+ )
28
34
 
29
- attr_accessor :activate_with
35
+ embedded_relations.each do |name, metadata|
36
+ Array.wrap(send(name)).each do |child|
37
+ if child.respond_to?(:changesets_with_children)
38
+ criteria.merge!(child.changesets_with_children)
39
+ end
40
+ end
41
+ end
42
+
43
+ criteria
30
44
  end
31
45
 
32
46
  # A hash of changes for being set on the changeset. It's just a filtered
@@ -35,17 +49,44 @@ module Workarea
35
49
  # @return [Hash]
36
50
  #
37
51
  def release_changes
38
- changes.keys.inject({}) do |memo, key|
39
- old_value, new_value = *changes[key]
52
+ ::Workarea::Release::Changes.new(self).to_h
53
+ end
40
54
 
41
- if Release::Changeset.track_change?(key, old_value, new_value)
42
- memo[key] = new_value
43
- end
55
+ def release_originals
56
+ ::Workarea::Release::Changes.new(self).to_originals_h
57
+ end
44
58
 
45
- memo
59
+ # Get a new instance of this model loaded with changes for the release
60
+ # passed in.
61
+ #
62
+ # @return [Releasable]
63
+ #
64
+ def in_release(release)
65
+ if release.blank? && !changed? # No extra work necessary, return a copy
66
+ result = dup
67
+ result.id = id
68
+ result.release_id = nil
69
+ result
70
+ elsif release.present? && !changed? # We don't have to reload from DB, just apply release changes to a copy
71
+ result = dup
72
+ result.id = id
73
+ result.release_id = release.id
74
+ release.preview.changesets_for(self).each { |cs| cs.apply_to(result) }
75
+ result
76
+ else
77
+ Release.with_current(release) { self.class.find(id) }
46
78
  end
47
79
  end
48
80
 
81
+ # Get a new instance of this model without any release changes. This a new
82
+ # instance without any release changes applied.
83
+ #
84
+ # @return [Releasable]
85
+ #
86
+ def without_release
87
+ in_release(nil)
88
+ end
89
+
49
90
  # Skip the release changeset for the duration of the block. Used when
50
91
  # publishing a changeset, i.e. don't apply/save the release changes since
51
92
  # we actually want to publish.
@@ -54,19 +95,10 @@ module Workarea
54
95
  #
55
96
  def skip_changeset
56
97
  @_skip_changeset = true
57
- result = yield
58
- @_skip_changeset = false
59
- result
60
- end
98
+ yield
61
99
 
62
- # Whether this model becomes active with the current release. Used for some
63
- # funny business when displaying content blocks in the admin. :(
64
- #
65
- # @return [Boolean]
66
- #
67
- def activates_with_current_release?
68
- return false if Release.current.blank?
69
- active? && active_changed? && !was_active?
100
+ ensure
101
+ @_skip_changeset = false
70
102
  end
71
103
 
72
104
  # Persist a to be recalled for publishing later. This is where changesets
@@ -76,34 +108,22 @@ module Workarea
76
108
  #
77
109
  # @param release_id [String]
78
110
  #
79
- def save_changeset(release_id)
80
- return unless release_id.present?
81
-
82
- changeset = Release::Changeset.find_or_initialize_by(
83
- releasable_id: id,
84
- releasable_type: self.class.name,
85
- release_id: release_id
86
- )
87
-
88
- if changeset.persisted? && release_changes.present?
89
- # This is to avoid triggering callbacks - calling #save on a
90
- # persisted Changeset is causing Mongoid to run callbacks on the
91
- # parent document, which resets the changeset changes to previous
92
- # values, not allowing updates to a changeset. #set merges a hash
93
- # field's new value into the old, so a call to #unset is necessary
94
- # to ensure any removed changes are properly deleted.
95
- #
96
- # TODO check in with this before v3 to see if Mongoid has fixed or
97
- # open a Mongoid PR
98
- #
99
- changeset.unset(:changeset)
100
- changeset.set(changeset: release_changes)
101
- elsif release_changes.present?
102
- changeset.document_path = document_path
103
- changeset.changeset = release_changes
104
- changeset.save!
105
- elsif changeset.persisted?
106
- changeset.destroy
111
+ def save_changeset(release)
112
+ return unless release.present?
113
+
114
+ changeset = release.changesets.find_or_initialize_by(releasable: self)
115
+
116
+ run_callbacks :save_release_changes do
117
+ if changeset.persisted? && release_changes.present?
118
+ changeset.update!(changeset: release_changes, original: release_originals)
119
+ elsif release_changes.present?
120
+ changeset.document_path = document_path
121
+ changeset.changeset = release_changes
122
+ changeset.original = release_originals
123
+ changeset.save!
124
+ elsif changeset.persisted?
125
+ changeset.destroy
126
+ end
107
127
  end
108
128
 
109
129
  changes.each do |field, change|
@@ -111,22 +131,25 @@ module Workarea
111
131
  end
112
132
  end
113
133
 
114
- def releasable?
115
- true
134
+ def destroy(*)
135
+ if embedded? && Release.current.present?
136
+ update!(active: false)
137
+ else
138
+ super
139
+ end
116
140
  end
117
141
 
118
142
  private
119
143
 
120
144
  def load_release_changes
121
- # Documents found with .only cause issues
122
- return if readonly? || Release.current.blank?
145
+ return if readonly? || Release.current.blank? # Documents found with .only cause issues
123
146
 
124
- changeset = changesets.find_by(release_id: Release.current.id) rescue nil
125
- changeset.apply_to(self) if changeset.present?
147
+ Release.current.preview.changesets_for(self).each { |c| c.apply_to(self) }
148
+ self.release_id = Release.current.id
126
149
  end
127
150
 
128
- def save_release_changes
129
- save_changeset(Release.current.try(:id)) unless @_skip_changeset
151
+ def handle_release_changes
152
+ save_changeset(Release.current) unless @_skip_changeset
130
153
  end
131
154
 
132
155
  def destroy_embedded_changesets
@@ -138,34 +161,5 @@ module Workarea
138
161
  errors.add(:slug, 'cannot be changed for releases')
139
162
  end
140
163
  end
141
-
142
- def save_activate_with
143
- self.active = false if activate_with?
144
- yield
145
- create_activation_changeset(activate_with) if activate_with?
146
- end
147
-
148
- def activate_with?
149
- activate_with.present? && BSON::ObjectId.legal?(activate_with)
150
- end
151
-
152
- def create_activation_changeset(release_id)
153
- set = changesets.find_or_initialize_by(release_id: release_id)
154
- set.document_path = document_path
155
-
156
- active_changeset = if Workarea.config.localized_active_fields
157
- { 'active' => { I18n.locale => true } }
158
- else
159
- { 'active' => true }
160
- end
161
-
162
- set.changeset = active_changeset
163
- set.save!
164
- end
165
-
166
- def was_active?
167
- (Workarea.config.localized_active_fields && active_was[I18n.locale]) ||
168
- (!Workarea.config.localized_active_fields && active_was)
169
- end
170
164
  end
171
165
  end
@@ -8,27 +8,25 @@ module Workarea
8
8
  field :publish_at, type: Time
9
9
  field :published_at, type: Time
10
10
  field :publish_job_id, type: String
11
- field :undo_at, type: Time
12
- field :undone_at, type: Time
13
- field :undo_job_id, type: String
11
+ field :undo_at, type: Time # TODO deprecated, remove in v3.6
12
+ field :undone_at, type: Time # TODO deprecated, remove in v3.6
13
+ field :undo_job_id, type: String # TODO deprecated, remove in v3.6
14
14
 
15
15
  has_many :changesets, class_name: 'Workarea::Release::Changeset'
16
+ has_one :undo, class_name: 'Workarea::Release', inverse_of: :undoes
17
+ belongs_to :undoes, class_name: 'Workarea::Release', inverse_of: :undo, optional: true
16
18
 
17
19
  index({ publish_at: 1 })
18
20
  index({ published_at: 1 })
19
- index({ undo_at: 1 })
20
- index({ undone_at: 1 })
21
21
 
22
22
  validates :name, presence: true
23
23
  validate :publish_at_status
24
- validate :undoable_release, if: :undo_at?
25
24
 
25
+ after_find :reset_preview
26
26
  before_save :remove_publish_job, if: Proc.new { |r| r.publish_at.blank? }
27
- before_save :remove_undo_job, if: Proc.new { |r| r.undo_at.blank? }
28
27
  before_save :schedule_publish
29
- before_save :schedule_undo
28
+ after_save :reset_preview
30
29
  before_destroy :remove_publish_job
31
- before_destroy :remove_undo_job
32
30
 
33
31
  scope :not_published, (lambda do
34
32
  any_of({ :published_at.exists => false }, { published_at: nil })
@@ -36,11 +34,21 @@ module Workarea
36
34
  scope :published, (lambda do
37
35
  where(:published_at.exists => true)
38
36
  end)
37
+ scope :published_between, ->(starts_at: nil, ends_at: nil) do
38
+ where(
39
+ :published_at.gte => starts_at,
40
+ :published_at.lte => ends_at
41
+ )
42
+ end
39
43
  scope :not_scheduled, (lambda do
40
44
  any_of({ :publish_at.exists => false }, { publish_at: nil })
41
45
  end)
42
- scope :scheduled, -> { where(:publish_at.gt => Time.current) }
43
- scope :to_undo, -> { where(:undo_at.gt => Time.current) }
46
+ scope :scheduled, ->(before: nil, after: nil) do
47
+ criteria = where(:publish_at.gt => Time.current)
48
+ criteria = criteria.where(:publish_at.lte => before) if before.present?
49
+ criteria = criteria.where(:publish_at.gte => after) if after.present?
50
+ criteria
51
+ end
44
52
  scope :soonest, -> { scheduled.asc(:publish_at) }
45
53
  scope :tomorrow, -> do
46
54
  where(
@@ -57,15 +65,28 @@ module Workarea
57
65
  Thread.current[:current_release] = release
58
66
  end
59
67
 
60
- def self.with_current(release_id, &block)
68
+ def self.with_current(release)
61
69
  previous = current
62
- self.current = release_id.blank? ? nil : find(release_id) rescue nil
63
70
 
64
- return_value = block.call
71
+ new_current = if release.is_a?(Release)
72
+ release
73
+ elsif release.present?
74
+ find(release) rescue nil
75
+ end
76
+
77
+ self.current = new_current
78
+ current&.reset_preview
79
+ yield
65
80
 
66
81
  ensure
67
82
  self.current = previous
68
- return_value
83
+
84
+ current&.reset_preview
85
+ previous&.reset_preview
86
+ end
87
+
88
+ def self.without_current(&block)
89
+ with_current(nil, &block)
69
90
  end
70
91
 
71
92
  # Gets a list of unscheduled releases
@@ -104,29 +125,12 @@ module Workarea
104
125
  results.uniq.sort_by { |r| [r.publish_at || 0, r.published_at || 0] }
105
126
  end
106
127
 
107
- # Get a list of releases undone or to be undone
108
- # within a given range
109
- #
110
- # @ return [Array<Release>]
111
- #
112
- def self.undone_within(start_date, end_date)
113
- results = where(
114
- :undo_at.gte => start_date.to_time,
115
- :undo_at.lte => end_date.to_time
116
- )
117
-
118
- results += where(
119
- :undone_at.gte => start_date.to_time,
120
- :undone_at.lte => end_date.to_time
121
- )
122
-
123
- results.uniq.sort_by { |r| [r.undo_at || 0, r.undone_at || 0] }
128
+ def self.sort_by_publish
129
+ scoped.sort_by { |r| [r.publish_at, r.created_at] }
124
130
  end
125
131
 
126
132
  def as_current
127
- self.class.with_current(id) do
128
- yield
129
- end
133
+ self.class.with_current(self) { yield }
130
134
  end
131
135
 
132
136
  def scheduled?
@@ -141,31 +145,48 @@ module Workarea
141
145
  scheduled? || (!scheduled? && !published?)
142
146
  end
143
147
 
144
- def undone?
145
- !!undone_at
146
- end
147
-
148
148
  def has_changes?
149
149
  changesets.present?
150
150
  end
151
151
 
152
- def publish!
153
- self.published_at = Time.current
154
- self.undone_at = nil
155
- self.publish_at = nil
156
- save!
152
+ def preview
153
+ @preview ||= Preview.new(self)
154
+ end
155
+
156
+ def reset_preview
157
+ @preview = nil
158
+ end
159
+
160
+ def scheduled_before
161
+ return [] unless scheduled?
162
+ self.class.scheduled(before: publish_at).ne(id: id).sort_by_publish
163
+ end
157
164
 
158
- ordered_changesets.each(&:publish!)
159
- touch_releasables
165
+ def scheduled_after
166
+ return [] unless scheduled?
167
+ self.class.scheduled(after: publish_at).ne(id: id).sort_by_publish
160
168
  end
161
169
 
162
- def undo!
163
- self.undo_at = nil if undo_at.present? && undo_at >= Time.current
164
- self.undone_at = Time.current
170
+ def previous
171
+ scheduled_before.last
172
+ end
173
+
174
+ def build_undo(attributes = {})
175
+ result = undo || Release.new(attributes)
176
+
177
+ result.name ||= I18n.t('workarea.release.undo', name: name)
178
+ result.tags = %w(undo) if result.tags.blank?
179
+ self.undo = result
180
+
181
+ result
182
+ end
183
+
184
+ def publish!
185
+ self.published_at = Time.current
186
+ self.publish_at = nil
165
187
  save!
166
188
 
167
- ordered_changesets.each(&:undo!)
168
- touch_releasables
189
+ changesets.each(&:publish!)
169
190
  end
170
191
 
171
192
  def set_publish_job
@@ -177,15 +198,6 @@ module Workarea
177
198
  )
178
199
  end
179
200
 
180
- def set_undo_job
181
- self.undo_job_id = Scheduler.schedule(
182
- worker: UndoRelease,
183
- at: undo_at,
184
- args: [id.to_s],
185
- job_id: undo_job_id
186
- )
187
- end
188
-
189
201
  # Get all statuses of this release.
190
202
  #
191
203
  # @return [Array<Symbol>]
@@ -195,18 +207,6 @@ module Workarea
195
207
  StatusCalculator.new(calculators, self).results
196
208
  end
197
209
 
198
- # Get changesets ordered based on publish priority set by configuration.
199
- #
200
- # @return [Array<Workarea::Release::Changeset>]
201
- #
202
- def ordered_changesets
203
- ordering = Workarea.config.release_changeset_ordering
204
-
205
- changesets.sort_by do |changeset|
206
- ordering[changeset.releasable_type].presence || 999
207
- end
208
- end
209
-
210
210
  private
211
211
 
212
212
  def publish_at_status
@@ -219,38 +219,11 @@ module Workarea
219
219
  set_publish_job if publish_at_changed? && publish_at.present?
220
220
  end
221
221
 
222
- def schedule_undo
223
- set_undo_job if undo_at_changed? && undo_at.present?
224
- end
225
-
226
222
  def remove_publish_job
227
223
  return if publish_job_id.blank?
228
224
 
229
225
  Scheduler.delete(publish_job_id)
230
226
  self.publish_job_id = nil
231
227
  end
232
-
233
- def remove_undo_job
234
- return if undo_job_id.blank?
235
-
236
- Scheduler.delete(undo_job_id)
237
- self.undo_job_id = nil
238
- end
239
-
240
- def undoable_release
241
- unless publish_at.present? || published_at.present?
242
- errors.add(:undo_at, I18n.t('workarea.errors.messages.undo_unpublished_release'))
243
- end
244
- end
245
-
246
- def touch_releasables
247
- Sidekiq::Callbacks.disable do
248
- changesets
249
- .map(&:releasable_from_document_path)
250
- .compact
251
- .uniq
252
- .each(&:touch)
253
- end
254
- end
255
228
  end
256
229
  end