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.
- checksums.yaml +7 -0
- data/.rubocop.yml +326 -0
- data/CHANGELOG.md +20501 -0
- data/README.md +163 -0
- data/docker-compose.yml +27 -0
- data/docs/Gemfile +8 -0
- data/docs/bin/middleman +29 -0
- data/docs/config.rb +87 -0
- data/docs/config.ru +7 -0
- data/docs/data/articles.yml +157 -0
- data/docs/package.json +15 -0
- data/docs/source/404.html.erb +13 -0
- data/docs/source/articles/access-routes-in-javascript.html.md +33 -0
- data/docs/source/articles/add-a-content-area.html.md +169 -0
- data/docs/source/articles/add-a-content-block-type.html.md +334 -0
- data/docs/source/articles/add-a-report.html.md +202 -0
- data/docs/source/articles/add-css-through-the-admin-ui.html.md +30 -0
- data/docs/source/articles/add-javascript-through-a-manifest.html.md +367 -0
- data/docs/source/articles/add-javascript-through-a-view.html.md +80 -0
- data/docs/source/articles/add-javascript-through-the-admin-ui.html.md +30 -0
- data/docs/source/articles/add-metrics.html.md +58 -0
- data/docs/source/articles/add-or-replace-a-pricing-calculator.html.md +150 -0
- data/docs/source/articles/add-remove-or-change-a-mongoid-validation.html.md +147 -0
- data/docs/source/articles/add-remove-or-change-a-product-template.html.md +142 -0
- data/docs/source/articles/add-remove-sort-and-group-storefront-search-filters.html.md +483 -0
- data/docs/source/articles/add-stylesheets-through-a-manifest.html.md +276 -0
- data/docs/source/articles/add-system-content.html.md +138 -0
- data/docs/source/articles/analytics-overview.html.md +51 -0
- data/docs/source/articles/analyze-storefront-search-results.html.md +261 -0
- data/docs/source/articles/api-overview.html.md +35 -0
- data/docs/source/articles/appending.html.md +506 -0
- data/docs/source/articles/application-document.html.md +88 -0
- data/docs/source/articles/automated-javascript-testing.html.md +162 -0
- data/docs/source/articles/b2b-overview.html.md +64 -0
- data/docs/source/articles/browser-and-device-support.html.md +47 -0
- data/docs/source/articles/change-product-placeholder-image.html.md +39 -0
- data/docs/source/articles/change-storefront-search-results.html.md +283 -0
- data/docs/source/articles/change-the-storefront-product-pricing-ui.html.md +348 -0
- data/docs/source/articles/change-the-storefront-search-filters-ui.html.md +103 -0
- data/docs/source/articles/checkout.html.md +479 -0
- data/docs/source/articles/commerce-model.html.md +164 -0
- data/docs/source/articles/configuration-for-hosting.html.md +106 -0
- data/docs/source/articles/configuration.html.md +406 -0
- data/docs/source/articles/configure-a-payment-gateway.html.md +58 -0
- data/docs/source/articles/configure-asset-storage.html.md +29 -0
- data/docs/source/articles/configure-asset-types.html.md +18 -0
- data/docs/source/articles/configure-contact-form-subjects-list.html.md +24 -0
- data/docs/source/articles/configure-imageoptim.html.md +23 -0
- data/docs/source/articles/configure-locales.html.md +45 -0
- data/docs/source/articles/configure-logins-and-authentication.html.md +42 -0
- data/docs/source/articles/configure-low-inventory-threshold.html.md +26 -0
- data/docs/source/articles/configure-product-image-sizes-and-processing.html.md +28 -0
- data/docs/source/articles/content.html.md +554 -0
- data/docs/source/articles/contentable.html.md +41 -0
- data/docs/source/articles/contribute-code.html.md +69 -0
- data/docs/source/articles/contribute-documentation.html.md +60 -0
- data/docs/source/articles/create-a-custom-discount.html.md +234 -0
- data/docs/source/articles/create-a-new-app.html.md +131 -0
- data/docs/source/articles/create-a-plugin.html.md +19 -0
- data/docs/source/articles/create-a-style-guide.html.md +71 -0
- data/docs/source/articles/create-a-theme.html.md +134 -0
- data/docs/source/articles/css-architectural-overview.html.md +89 -0
- data/docs/source/articles/customize-a-helper.html.md +91 -0
- data/docs/source/articles/decoration.html.md +415 -0
- data/docs/source/articles/define-and-configure-inventory-policies.html.md +107 -0
- data/docs/source/articles/documentation-style-guide.html.md +48 -0
- data/docs/source/articles/documentation.html.md +54 -0
- data/docs/source/articles/domain-modeling.html.md +82 -0
- data/docs/source/articles/error-pages.html.md.erb +95 -0
- data/docs/source/articles/extension-overview.html.md +152 -0
- data/docs/source/articles/favicon-support.html.md +112 -0
- data/docs/source/articles/feature-spec-helper-stylesheet.html.md +25 -0
- data/docs/source/articles/featurejs-and-feature-spec-helper.html.md +20 -0
- data/docs/source/articles/help-and-support.html.md +34 -0
- data/docs/source/articles/html-fragment-caching.html.md +46 -0
- data/docs/source/articles/http-caching.html.md +43 -0
- data/docs/source/articles/i18n.html.md +35 -0
- data/docs/source/articles/images-flow.html.md +10 -0
- data/docs/source/articles/index-storefront-search-documents.html.md +104 -0
- data/docs/source/articles/infrastructure.html.md +46 -0
- data/docs/source/articles/installing.html.md +61 -0
- data/docs/source/articles/integrate-a-payment-gateway.html.md +124 -0
- data/docs/source/articles/integrate-a-web-analytics-provider.html.md +35 -0
- data/docs/source/articles/integrate-an-inventory-management-system.html.md +88 -0
- data/docs/source/articles/integrating-with-other-software.html.md +59 -0
- data/docs/source/articles/inventory.html.md +352 -0
- data/docs/source/articles/javascript-coding-standards.html.md +30 -0
- data/docs/source/articles/javascript-modules.html.md +174 -0
- data/docs/source/articles/javascript-overview.html.md +62 -0
- data/docs/source/articles/javascript-reference-documentation.html.md +51 -0
- data/docs/source/articles/javascript-templates.html.md +52 -0
- data/docs/source/articles/low-level-caching.html.md +25 -0
- data/docs/source/articles/maintain-a-plugin.html.md +12 -0
- data/docs/source/articles/maintenance-policy.html.md +79 -0
- data/docs/source/articles/navigable.html.md +51 -0
- data/docs/source/articles/navigating-the-code.html.md +149 -0
- data/docs/source/articles/navigation.html.md +386 -0
- data/docs/source/articles/order-life-cycle.html.md +546 -0
- data/docs/source/articles/order-pricing.html.md +389 -0
- data/docs/source/articles/orders-and-items.html.md +210 -0
- data/docs/source/articles/orders.html.md +66 -0
- data/docs/source/articles/overriding.html.md +155 -0
- data/docs/source/articles/overview.html.md +43 -0
- data/docs/source/articles/plugins-overview.html.md +12 -0
- data/docs/source/articles/prerequisites-and-dependencies.html.md +202 -0
- data/docs/source/articles/products.html.md.erb +1270 -0
- data/docs/source/articles/progressive-web-application-support.html.md +148 -0
- data/docs/source/articles/rails-asset-manifests.html.md +33 -0
- data/docs/source/articles/rails-asset-view-helpers.html.md +25 -0
- data/docs/source/articles/reading-data.html.md +10 -0
- data/docs/source/articles/releasable.html.md +37 -0
- data/docs/source/articles/report-a-bug.html.md +75 -0
- data/docs/source/articles/ruby-coding-standards.html.md +10 -0
- data/docs/source/articles/run-sidekiq-in-a-local-environment.html.md +40 -0
- data/docs/source/articles/searching.html.md +1005 -0
- data/docs/source/articles/security-policy.html.md +42 -0
- data/docs/source/articles/seeds.html.md +345 -0
- data/docs/source/articles/shipping.html.md +756 -0
- data/docs/source/articles/sort-and-exclude-product-options.html.md +47 -0
- data/docs/source/articles/storefront-search-features.html.md +568 -0
- data/docs/source/articles/storefront-searches.html.md +126 -0
- data/docs/source/articles/style-guides.html.md +21 -0
- data/docs/source/articles/stylesheet-coding-standards.html.md +24 -0
- data/docs/source/articles/stylesheets-overview.html.md +67 -0
- data/docs/source/articles/swappable-list-data-structure.html.md +81 -0
- data/docs/source/articles/system-emails.html.md +102 -0
- data/docs/source/articles/taggable.html.md +8 -0
- data/docs/source/articles/test-a-credit-card-transaction.html.md +16 -0
- data/docs/source/articles/test-if-a-plugin-is-installed.html.md +34 -0
- data/docs/source/articles/testing.html.md +914 -0
- data/docs/source/articles/themes-overview.html.md +155 -0
- data/docs/source/articles/translate-administrable-content.html.md +14 -0
- data/docs/source/articles/translate-javascript-content.html.md +16 -0
- data/docs/source/articles/translate-or-customize-message-content.html.md +29 -0
- data/docs/source/articles/translate-or-customize-static-content.html.md +30 -0
- data/docs/source/articles/use-an-existing-workarea-app.html.md +108 -0
- data/docs/source/articles/view-models.html.md +509 -0
- data/docs/source/articles/views.html.md +14 -0
- data/docs/source/articles/workers.html.md +613 -0
- data/docs/source/articles/writing-data.html.md +10 -0
- data/docs/source/cli.html.md +163 -0
- data/docs/source/favicon.ico +0 -0
- data/docs/source/images/3-variants-1-option.png +0 -0
- data/docs/source/images/3-variants-3-options.png +0 -0
- data/docs/source/images/3-years-primary-image.png +0 -0
- data/docs/source/images/404-storefront-error-page.png +0 -0
- data/docs/source/images/404-system-content-admin.png +0 -0
- data/docs/source/images/404.jpg +0 -0
- data/docs/source/images/5-years-primary-image.png +0 -0
- data/docs/source/images/activity-dashboard.png +0 -0
- data/docs/source/images/activity-for-object.png +0 -0
- data/docs/source/images/activity-ui.png +0 -0
- data/docs/source/images/adding-captioned-image-block-custom-icon.png +0 -0
- data/docs/source/images/adding-captioned-image-block-default-icon.png +0 -0
- data/docs/source/images/admin-alerts-ui.png +0 -0
- data/docs/source/images/admin-category-range-filters.png +0 -0
- data/docs/source/images/admin-for-3-column-hero.png +0 -0
- data/docs/source/images/admin-help-index.png +0 -0
- data/docs/source/images/admin-help-ui.png +0 -0
- data/docs/source/images/admin-javascript.png +0 -0
- data/docs/source/images/admin-notification-for-deactivated-discount.png +0 -0
- data/docs/source/images/admin-notifications-ui.png +0 -0
- data/docs/source/images/admin-product-show-page.png +0 -0
- data/docs/source/images/admin-products-index-page.png +0 -0
- data/docs/source/images/admin-range-filters.png +0 -0
- data/docs/source/images/admin-style-guides-navigation.png +0 -0
- data/docs/source/images/after-re-seeding.png +0 -0
- data/docs/source/images/after-seeding-localhost-3000.png +0 -0
- data/docs/source/images/after-seeding.png +0 -0
- data/docs/source/images/arrow.svg +1 -0
- data/docs/source/images/arrow_white.svg +1 -0
- data/docs/source/images/aws-resource-map.png +0 -0
- data/docs/source/images/backordered-until-output-on-inventory-sku-card.png +0 -0
- data/docs/source/images/before-seeding-localhost-3000.png +0 -0
- data/docs/source/images/before-seeding.png +0 -0
- data/docs/source/images/browsing-workarea-versions-on-the-web.png +0 -0
- data/docs/source/images/bulk-asset-upload-on-assets-index-page.png +0 -0
- data/docs/source/images/bulk-asset-upload-while-editing-content.png +0 -0
- data/docs/source/images/bundle-show-workarea-core.png +0 -0
- data/docs/source/images/bundle-show-workarea.png +0 -0
- data/docs/source/images/calendar-for-backordered-until-field.png +0 -0
- data/docs/source/images/captioned-image-block-in-storefront.png +0 -0
- data/docs/source/images/captioned-image-content-block-storefront-component-style-guide.png +0 -0
- data/docs/source/images/cart-system-content-in-admin.png +0 -0
- data/docs/source/images/cart-system-content-in-storefront.png +0 -0
- data/docs/source/images/checkout-addresses-guest.png +0 -0
- data/docs/source/images/checkout-addresses-user.png +0 -0
- data/docs/source/images/checkout-confirmation.png +0 -0
- data/docs/source/images/checkout-flow-0.png +0 -0
- data/docs/source/images/checkout-flow-1.png +0 -0
- data/docs/source/images/checkout-flow-2.png +0 -0
- data/docs/source/images/checkout-flow-3.png +0 -0
- data/docs/source/images/checkout-flow-4.png +0 -0
- data/docs/source/images/checkout-payment-guest.png +0 -0
- data/docs/source/images/checkout-payment-user.png +0 -0
- data/docs/source/images/checkout-shipping.png +0 -0
- data/docs/source/images/color-picker-component-admin-style-guide.png +0 -0
- data/docs/source/images/color-picker-component-on-content-editing-screen.png +0 -0
- data/docs/source/images/commerce-model-carts-orders.png +0 -0
- data/docs/source/images/commerce-model-order-pricing.png +0 -0
- data/docs/source/images/commerce-model.png +0 -0
- data/docs/source/images/configuring-an-index-pattern-in-kibana.png +0 -0
- data/docs/source/images/content-block-presets.png +0 -0
- data/docs/source/images/content-search-customization.png +0 -0
- data/docs/source/images/country-with-region-data-in-address-form.png +0 -0
- data/docs/source/images/country-without-region-data-in-address-form.png +0 -0
- data/docs/source/images/create-content-block-preset-ui.png +0 -0
- data/docs/source/images/credit-card-icons.png +0 -0
- data/docs/source/images/css-added-through-admin.png +0 -0
- data/docs/source/images/css-admin-ui.png +0 -0
- data/docs/source/images/current-configuration-shown-in-admin-settings.png +0 -0
- data/docs/source/images/customer-impersonation-in-admin.png +0 -0
- data/docs/source/images/customer-impersonation-in-store-front.png +0 -0
- data/docs/source/images/date-filter-same-day.png +0 -0
- data/docs/source/images/developer-toolbar-in-store-front.png +0 -0
- data/docs/source/images/discounts-sorted-by-most-redeemed.png +0 -0
- data/docs/source/images/edit-help-article.png +0 -0
- data/docs/source/images/editing-content-for-search-customization.png +0 -0
- data/docs/source/images/editing-dynamic-captioned-image-block.png +0 -0
- data/docs/source/images/editing-product-fields-in-the-admin.png +0 -0
- data/docs/source/images/editing-search-system-content.png +0 -0
- data/docs/source/images/editing-static-captioned-image-block-custom-icon.png +0 -0
- data/docs/source/images/editing-static-captioned-image-block-default-icon.png +0 -0
- data/docs/source/images/external.svg +1 -0
- data/docs/source/images/favicon_16.png +0 -0
- data/docs/source/images/favicon_180.png +0 -0
- data/docs/source/images/favicon_32.png +0 -0
- data/docs/source/images/filters-all.png +0 -0
- data/docs/source/images/filters-control.png +0 -0
- data/docs/source/images/filters-custom.png +0 -0
- data/docs/source/images/filters-groups.png +0 -0
- data/docs/source/images/filters-material.png +0 -0
- data/docs/source/images/filters-omitted.png +0 -0
- data/docs/source/images/filters-pinned.png +0 -0
- data/docs/source/images/filters-range.png +0 -0
- data/docs/source/images/filters-sorted.png +0 -0
- data/docs/source/images/filters-wrapping-to-second-line-in-admin.png +0 -0
- data/docs/source/images/generic-product-template-images-no-options-selected.png +0 -0
- data/docs/source/images/generic-product-template-images-options-selected.png +0 -0
- data/docs/source/images/generic-template.png +0 -0
- data/docs/source/images/hosting.svg +1 -0
- data/docs/source/images/image-group-content-block-in-storefront.png +0 -0
- data/docs/source/images/images.svg +1 -0
- data/docs/source/images/import-export-screenshot.png +0 -0
- data/docs/source/images/invalid-display.png +0 -0
- data/docs/source/images/itcss.png +0 -0
- data/docs/source/images/kibana-dev-tools-console.png +0 -0
- data/docs/source/images/layout-content-admin-with-2-areas.png +0 -0
- data/docs/source/images/layout-content-admin-with-3-areas.png +0 -0
- data/docs/source/images/link-to-search-system-content.png +0 -0
- data/docs/source/images/logo.svg +1 -0
- data/docs/source/images/menu.svg +2 -0
- data/docs/source/images/mongo-replica-set.svg +1 -0
- data/docs/source/images/multi-column-hero-blocks.png +0 -0
- data/docs/source/images/option-selects-product-template-images-options-selected.png +0 -0
- data/docs/source/images/option-selects-template.png +0 -0
- data/docs/source/images/option-thumbnails-template.png +0 -0
- data/docs/source/images/order-item-total-price-diagram.png +0 -0
- data/docs/source/images/order-pricing-cart-example.png +0 -0
- data/docs/source/images/order-pricing-example-adjustments.png +0 -0
- data/docs/source/images/order-pricing-example-totals.png +0 -0
- data/docs/source/images/order-pricing-placed-order-example.png +0 -0
- data/docs/source/images/order-shipping-total-diagram.png +0 -0
- data/docs/source/images/order-show-with-multiple-tenders.png +0 -0
- data/docs/source/images/order-subtotal-price-diagram.png +0 -0
- data/docs/source/images/order-tax-total-diagram.png +0 -0
- data/docs/source/images/order-total-price-diagram.png +0 -0
- data/docs/source/images/order-total-value-diagram.png +0 -0
- data/docs/source/images/orders-dashboard-links.png +0 -0
- data/docs/source/images/oval.svg +1 -0
- data/docs/source/images/payment-icon-storefront-style-guide.png +0 -0
- data/docs/source/images/people-dashboard-links.png +0 -0
- data/docs/source/images/price-adjustments-diagram.png +0 -0
- data/docs/source/images/price-display-no-options.png +0 -0
- data/docs/source/images/price-display-options-selected.png +0 -0
- data/docs/source/images/pricing-calculators-diagram.png +0 -0
- data/docs/source/images/product-list-content-block-admin.png +0 -0
- data/docs/source/images/product-list-content-block-in-store-front.png +0 -0
- data/docs/source/images/promo-products-excluded-autocomplete-results-after.png +0 -0
- data/docs/source/images/promo-products-excluded-featured-category-results-after.png +0 -0
- data/docs/source/images/promo-products-excluded-recommendations-results-after.png +0 -0
- data/docs/source/images/promo-products-excluded-search-category-results-after.png +0 -0
- data/docs/source/images/promo-products-excluded-search-results-after.png +0 -0
- data/docs/source/images/promo-products-included-autocomplete-results-before.png +0 -0
- data/docs/source/images/promo-products-included-featured-category-results-before.png +0 -0
- data/docs/source/images/promo-products-included-recommendations-results-before.png +0 -0
- data/docs/source/images/promo-products-included-search-category-results-before.png +0 -0
- data/docs/source/images/promo-products-included-search-results-before.png +0 -0
- data/docs/source/images/rails-version-constraint.png +0 -0
- data/docs/source/images/re-enable-discount.png +0 -0
- data/docs/source/images/reading-data.svg +1 -0
- data/docs/source/images/readme-hero.png +0 -0
- data/docs/source/images/redesigned-customized-sort-for-search-results.png +0 -0
- data/docs/source/images/reviews-summary-above-share-buttons.png +0 -0
- data/docs/source/images/reviews-summary-below-product-name.png +0 -0
- data/docs/source/images/reviews-summary-below-share-buttons.png +0 -0
- data/docs/source/images/reviews-summary-removed.png +0 -0
- data/docs/source/images/rsa-fingerprint-for-stash.png +0 -0
- data/docs/source/images/ruby-version-constraint.png +0 -0
- data/docs/source/images/script-tag-added-through-admin.png +0 -0
- data/docs/source/images/search-analysis-admin-alternate-rendering.png +0 -0
- data/docs/source/images/search-analysis-admin.png +0 -0
- data/docs/source/images/search-quality-report.png +0 -0
- data/docs/source/images/search.svg +1 -0
- data/docs/source/images/searching-for-cart-system-content-in-admin.png +0 -0
- data/docs/source/images/searching-for-layout-system-content-in-admin.png +0 -0
- data/docs/source/images/seeded-admin.png +0 -0
- data/docs/source/images/seeds-from-plugins.png +0 -0
- data/docs/source/images/seo-metadata-automation-ui.png +0 -0
- data/docs/source/images/show-password-button.png +0 -0
- data/docs/source/images/storefront-autocomplete.png +0 -0
- data/docs/source/images/storefront-category-summary-content-block.png +0 -0
- data/docs/source/images/storefront-category.png +0 -0
- data/docs/source/images/storefront-product-after-overriding.png +0 -0
- data/docs/source/images/storefront-product-before-overriding.png +0 -0
- data/docs/source/images/storefront-product-browse-page.png +0 -0
- data/docs/source/images/storefront-product-recommendations.png +0 -0
- data/docs/source/images/storefront-product-show-page.png +0 -0
- data/docs/source/images/storefront-requests-and-search-requests.png +0 -0
- data/docs/source/images/storefront-search-request-handling.png +0 -0
- data/docs/source/images/storefront-search-response-creation.png +0 -0
- data/docs/source/images/storefront-search.png +0 -0
- data/docs/source/images/storefront-style-guides-navigation.png +0 -0
- data/docs/source/images/styles.css +3 -0
- data/docs/source/images/tax-categories-ui.png +0 -0
- data/docs/source/images/tax-rates-ui.png +0 -0
- data/docs/source/images/unpurchasable-product.png +0 -0
- data/docs/source/images/url-redirects-filtering.png +0 -0
- data/docs/source/images/utility-nav-area-in-admin.png +0 -0
- data/docs/source/images/utility-nav-area-in-storefront.png +0 -0
- data/docs/source/images/validation-message-in-storefront.png +0 -0
- data/docs/source/images/view-model-interface.png +0 -0
- data/docs/source/images/viewing-workarea-version-in-source.png +0 -0
- data/docs/source/images/workarea.svg +1 -0
- data/docs/source/images/worst-performing-searches-on-results-customization-page.png +0 -0
- data/docs/source/images/writing-data.svg +1 -0
- data/docs/source/index.html.erb +167 -0
- data/docs/source/javascripts/jquery.js +2 -0
- data/docs/source/javascripts/lunr.js +7 -0
- data/docs/source/javascripts/site.js +299 -0
- data/docs/source/javascripts/vendor/highlight.pack.js +2 -0
- data/docs/source/layouts/article.erb +106 -0
- data/docs/source/layouts/bare.erb +46 -0
- data/docs/source/layouts/layout.erb +43 -0
- data/docs/source/release-notes.html.md +258 -0
- data/docs/source/release-notes/workarea-3-0-0.html.md +146 -0
- data/docs/source/release-notes/workarea-3-0-1.html.md +161 -0
- data/docs/source/release-notes/workarea-3-0-10.html.md +39 -0
- data/docs/source/release-notes/workarea-3-0-11.html.md +277 -0
- data/docs/source/release-notes/workarea-3-0-12.html.md +14 -0
- data/docs/source/release-notes/workarea-3-0-13.html.md +153 -0
- data/docs/source/release-notes/workarea-3-0-14.html.md +93 -0
- data/docs/source/release-notes/workarea-3-0-15.html.md +107 -0
- data/docs/source/release-notes/workarea-3-0-16.html.md +36 -0
- data/docs/source/release-notes/workarea-3-0-17.html.md +141 -0
- data/docs/source/release-notes/workarea-3-0-18.html.md +123 -0
- data/docs/source/release-notes/workarea-3-0-19.html.md +160 -0
- data/docs/source/release-notes/workarea-3-0-2.html.md +222 -0
- data/docs/source/release-notes/workarea-3-0-20.html.md +95 -0
- data/docs/source/release-notes/workarea-3-0-21.html.md +168 -0
- data/docs/source/release-notes/workarea-3-0-22.html.md +268 -0
- data/docs/source/release-notes/workarea-3-0-23.html.md +173 -0
- data/docs/source/release-notes/workarea-3-0-24.html.md +19 -0
- data/docs/source/release-notes/workarea-3-0-25.html.md +26 -0
- data/docs/source/release-notes/workarea-3-0-26.html.md +199 -0
- data/docs/source/release-notes/workarea-3-0-27.html.md +113 -0
- data/docs/source/release-notes/workarea-3-0-28.html.md +39 -0
- data/docs/source/release-notes/workarea-3-0-29.html.md +73 -0
- data/docs/source/release-notes/workarea-3-0-3.html.md +35 -0
- data/docs/source/release-notes/workarea-3-0-30.html.md +186 -0
- data/docs/source/release-notes/workarea-3-0-31.html.md +125 -0
- data/docs/source/release-notes/workarea-3-0-32.html.md +73 -0
- data/docs/source/release-notes/workarea-3-0-33.html.md +137 -0
- data/docs/source/release-notes/workarea-3-0-34.html.md +203 -0
- data/docs/source/release-notes/workarea-3-0-35.html.md +205 -0
- data/docs/source/release-notes/workarea-3-0-36.html.md +105 -0
- data/docs/source/release-notes/workarea-3-0-37.html.md +144 -0
- data/docs/source/release-notes/workarea-3-0-38.html.md +73 -0
- data/docs/source/release-notes/workarea-3-0-39.html.md +77 -0
- data/docs/source/release-notes/workarea-3-0-4.html.md +14 -0
- data/docs/source/release-notes/workarea-3-0-40.html.md +130 -0
- data/docs/source/release-notes/workarea-3-0-41.html.md +70 -0
- data/docs/source/release-notes/workarea-3-0-42.html.md +52 -0
- data/docs/source/release-notes/workarea-3-0-43.html.md +72 -0
- data/docs/source/release-notes/workarea-3-0-44.html.md +93 -0
- data/docs/source/release-notes/workarea-3-0-45.html.md +61 -0
- data/docs/source/release-notes/workarea-3-0-46.html.md +171 -0
- data/docs/source/release-notes/workarea-3-0-47.html.md +130 -0
- data/docs/source/release-notes/workarea-3-0-48.html.md +160 -0
- data/docs/source/release-notes/workarea-3-0-49.html.md +28 -0
- data/docs/source/release-notes/workarea-3-0-5.html.md +225 -0
- data/docs/source/release-notes/workarea-3-0-50.html.md +74 -0
- data/docs/source/release-notes/workarea-3-0-51.html.md +61 -0
- data/docs/source/release-notes/workarea-3-0-52.html.md +76 -0
- data/docs/source/release-notes/workarea-3-0-53.html.md +126 -0
- data/docs/source/release-notes/workarea-3-0-54.html.md +112 -0
- data/docs/source/release-notes/workarea-3-0-55.html.md +105 -0
- data/docs/source/release-notes/workarea-3-0-56.html.md +56 -0
- data/docs/source/release-notes/workarea-3-0-57.html.md +82 -0
- data/docs/source/release-notes/workarea-3-0-58.html.md +153 -0
- data/docs/source/release-notes/workarea-3-0-59.html.md +78 -0
- data/docs/source/release-notes/workarea-3-0-6.html.md +165 -0
- data/docs/source/release-notes/workarea-3-0-60.html.md +43 -0
- data/docs/source/release-notes/workarea-3-0-61.html.md +46 -0
- data/docs/source/release-notes/workarea-3-0-62.html.md +23 -0
- data/docs/source/release-notes/workarea-3-0-63.html.md +25 -0
- data/docs/source/release-notes/workarea-3-0-64.html.md +25 -0
- data/docs/source/release-notes/workarea-3-0-65.html.md +37 -0
- data/docs/source/release-notes/workarea-3-0-7.html.md +207 -0
- data/docs/source/release-notes/workarea-3-0-8.html.md +337 -0
- data/docs/source/release-notes/workarea-3-0-9.html.md +196 -0
- data/docs/source/release-notes/workarea-3-1-0.html.md +414 -0
- data/docs/source/release-notes/workarea-3-1-1.html.md +139 -0
- data/docs/source/release-notes/workarea-3-1-10.html.md +19 -0
- data/docs/source/release-notes/workarea-3-1-11.html.md +27 -0
- data/docs/source/release-notes/workarea-3-1-12.html.md +216 -0
- data/docs/source/release-notes/workarea-3-1-13.html.md +113 -0
- data/docs/source/release-notes/workarea-3-1-14.html.md +39 -0
- data/docs/source/release-notes/workarea-3-1-15.html.md +107 -0
- data/docs/source/release-notes/workarea-3-1-16.html.md +188 -0
- data/docs/source/release-notes/workarea-3-1-17.html.md +141 -0
- data/docs/source/release-notes/workarea-3-1-18.html.md +73 -0
- data/docs/source/release-notes/workarea-3-1-19.html.md +137 -0
- data/docs/source/release-notes/workarea-3-1-2.html.md +55 -0
- data/docs/source/release-notes/workarea-3-1-20.html.md +203 -0
- data/docs/source/release-notes/workarea-3-1-21.html.md +205 -0
- data/docs/source/release-notes/workarea-3-1-22.html.md +121 -0
- data/docs/source/release-notes/workarea-3-1-23.html.md +144 -0
- data/docs/source/release-notes/workarea-3-1-24.html.md +94 -0
- data/docs/source/release-notes/workarea-3-1-25.html.md +77 -0
- data/docs/source/release-notes/workarea-3-1-26.html.md +130 -0
- data/docs/source/release-notes/workarea-3-1-27.html.md +70 -0
- data/docs/source/release-notes/workarea-3-1-28.html.md +52 -0
- data/docs/source/release-notes/workarea-3-1-29.html.md +44 -0
- data/docs/source/release-notes/workarea-3-1-3.html.md +185 -0
- data/docs/source/release-notes/workarea-3-1-30.html.md +72 -0
- data/docs/source/release-notes/workarea-3-1-31.html.md +93 -0
- data/docs/source/release-notes/workarea-3-1-32.html.md +61 -0
- data/docs/source/release-notes/workarea-3-1-33.html.md +171 -0
- data/docs/source/release-notes/workarea-3-1-34.html.md +130 -0
- data/docs/source/release-notes/workarea-3-1-35.html.md +179 -0
- data/docs/source/release-notes/workarea-3-1-36.html.md +28 -0
- data/docs/source/release-notes/workarea-3-1-37.html.md +74 -0
- data/docs/source/release-notes/workarea-3-1-38.html.md +61 -0
- data/docs/source/release-notes/workarea-3-1-39.html.md +96 -0
- data/docs/source/release-notes/workarea-3-1-4.html.md +148 -0
- data/docs/source/release-notes/workarea-3-1-40.html.md +126 -0
- data/docs/source/release-notes/workarea-3-1-41.html.md +128 -0
- data/docs/source/release-notes/workarea-3-1-42.html.md +105 -0
- data/docs/source/release-notes/workarea-3-1-43.html.md +37 -0
- data/docs/source/release-notes/workarea-3-1-44.html.md +82 -0
- data/docs/source/release-notes/workarea-3-1-45.html.md +153 -0
- data/docs/source/release-notes/workarea-3-1-46.html.md +91 -0
- data/docs/source/release-notes/workarea-3-1-47.html.md +65 -0
- data/docs/source/release-notes/workarea-3-1-48.html.md +46 -0
- data/docs/source/release-notes/workarea-3-1-49.html.md +23 -0
- data/docs/source/release-notes/workarea-3-1-5.html.md +169 -0
- data/docs/source/release-notes/workarea-3-1-50.html.md +42 -0
- data/docs/source/release-notes/workarea-3-1-51.html.md +25 -0
- data/docs/source/release-notes/workarea-3-1-52.html.md +57 -0
- data/docs/source/release-notes/workarea-3-1-6.html.md +117 -0
- data/docs/source/release-notes/workarea-3-1-7.html.md +176 -0
- data/docs/source/release-notes/workarea-3-1-8.html.md +283 -0
- data/docs/source/release-notes/workarea-3-1-9.html.md +212 -0
- data/docs/source/release-notes/workarea-3-2-0.html.md +1705 -0
- data/docs/source/release-notes/workarea-3-2-1.html.md +216 -0
- data/docs/source/release-notes/workarea-3-2-10.html.md +237 -0
- data/docs/source/release-notes/workarea-3-2-11.html.md +121 -0
- data/docs/source/release-notes/workarea-3-2-12.html.md +145 -0
- data/docs/source/release-notes/workarea-3-2-13.html.md +138 -0
- data/docs/source/release-notes/workarea-3-2-14.html.md +77 -0
- data/docs/source/release-notes/workarea-3-2-15.html.md +130 -0
- data/docs/source/release-notes/workarea-3-2-16.html.md +111 -0
- data/docs/source/release-notes/workarea-3-2-17.html.md +52 -0
- data/docs/source/release-notes/workarea-3-2-18.html.md +44 -0
- data/docs/source/release-notes/workarea-3-2-19.html.md +72 -0
- data/docs/source/release-notes/workarea-3-2-2.html.md +145 -0
- data/docs/source/release-notes/workarea-3-2-20.html.md +93 -0
- data/docs/source/release-notes/workarea-3-2-21.html.md +61 -0
- data/docs/source/release-notes/workarea-3-2-22.html.md +154 -0
- data/docs/source/release-notes/workarea-3-2-23.html.md +130 -0
- data/docs/source/release-notes/workarea-3-2-24.html.md +200 -0
- data/docs/source/release-notes/workarea-3-2-25.html.md +28 -0
- data/docs/source/release-notes/workarea-3-2-26.html.md +94 -0
- data/docs/source/release-notes/workarea-3-2-27.html.md +61 -0
- data/docs/source/release-notes/workarea-3-2-28.html.md +96 -0
- data/docs/source/release-notes/workarea-3-2-29.html.md +126 -0
- data/docs/source/release-notes/workarea-3-2-30.html.md +112 -0
- data/docs/source/release-notes/workarea-3-2-31.html.md +105 -0
- data/docs/source/release-notes/workarea-3-2-32.html.md +56 -0
- data/docs/source/release-notes/workarea-3-2-33.html.md +82 -0
- data/docs/source/release-notes/workarea-3-2-34.html.md +153 -0
- data/docs/source/release-notes/workarea-3-2-35.html.md +91 -0
- data/docs/source/release-notes/workarea-3-2-36.html.md +118 -0
- data/docs/source/release-notes/workarea-3-2-37.html.md +46 -0
- data/docs/source/release-notes/workarea-3-2-38.html.md +23 -0
- data/docs/source/release-notes/workarea-3-2-39.html.md +42 -0
- data/docs/source/release-notes/workarea-3-2-4.html.md +109 -0
- data/docs/source/release-notes/workarea-3-2-40.html.md +25 -0
- data/docs/source/release-notes/workarea-3-2-41.html.md +90 -0
- data/docs/source/release-notes/workarea-3-2-5.html.md +186 -0
- data/docs/source/release-notes/workarea-3-2-6.html.md +173 -0
- data/docs/source/release-notes/workarea-3-2-7.html.md +89 -0
- data/docs/source/release-notes/workarea-3-2-8.html.md +137 -0
- data/docs/source/release-notes/workarea-3-2-9.html.md +219 -0
- data/docs/source/release-notes/workarea-3-3-0.html.md +1272 -0
- data/docs/source/release-notes/workarea-3-3-1.html.md +324 -0
- data/docs/source/release-notes/workarea-3-3-10.html.md +69 -0
- data/docs/source/release-notes/workarea-3-3-11.html.md +72 -0
- data/docs/source/release-notes/workarea-3-3-12.html.md +136 -0
- data/docs/source/release-notes/workarea-3-3-13.html.md +61 -0
- data/docs/source/release-notes/workarea-3-3-14.html.md +196 -0
- data/docs/source/release-notes/workarea-3-3-15.html.md +167 -0
- data/docs/source/release-notes/workarea-3-3-16.html.md +234 -0
- data/docs/source/release-notes/workarea-3-3-17.html.md +82 -0
- data/docs/source/release-notes/workarea-3-3-18.html.md +165 -0
- data/docs/source/release-notes/workarea-3-3-19.html.md +106 -0
- data/docs/source/release-notes/workarea-3-3-2.html.md +72 -0
- data/docs/source/release-notes/workarea-3-3-20.html.md +116 -0
- data/docs/source/release-notes/workarea-3-3-21.html.md +228 -0
- data/docs/source/release-notes/workarea-3-3-22.html.md +125 -0
- data/docs/source/release-notes/workarea-3-3-23.html.md +154 -0
- data/docs/source/release-notes/workarea-3-3-24.html.md +70 -0
- data/docs/source/release-notes/workarea-3-3-25.html.md +114 -0
- data/docs/source/release-notes/workarea-3-3-26.html.md +260 -0
- data/docs/source/release-notes/workarea-3-3-27.html.md +138 -0
- data/docs/source/release-notes/workarea-3-3-28.html.md +147 -0
- data/docs/source/release-notes/workarea-3-3-29.html.md +63 -0
- data/docs/source/release-notes/workarea-3-3-3.html.md +153 -0
- data/docs/source/release-notes/workarea-3-3-30.html.md +102 -0
- data/docs/source/release-notes/workarea-3-3-31.html.md +57 -0
- data/docs/source/release-notes/workarea-3-3-32.html.md +44 -0
- data/docs/source/release-notes/workarea-3-3-33.html.md +114 -0
- data/docs/source/release-notes/workarea-3-3-4.html.md +332 -0
- data/docs/source/release-notes/workarea-3-3-5.html.md +242 -0
- data/docs/source/release-notes/workarea-3-3-6.html.md +100 -0
- data/docs/source/release-notes/workarea-3-3-7.html.md +148 -0
- data/docs/source/release-notes/workarea-3-3-8.html.md +163 -0
- data/docs/source/release-notes/workarea-3-3-9.html.md +93 -0
- data/docs/source/release-notes/workarea-3-4-0.html.md +580 -0
- data/docs/source/release-notes/workarea-3-4-1.html.md +150 -0
- data/docs/source/release-notes/workarea-3-4-10.html.md +72 -0
- data/docs/source/release-notes/workarea-3-4-11.html.md +60 -0
- data/docs/source/release-notes/workarea-3-4-12.html.md +155 -0
- data/docs/source/release-notes/workarea-3-4-2.html.md +188 -0
- data/docs/source/release-notes/workarea-3-4-3.html.md +136 -0
- data/docs/source/release-notes/workarea-3-4-4.html.md +114 -0
- data/docs/source/release-notes/workarea-3-4-5.html.md +275 -0
- data/docs/source/release-notes/workarea-3-4-6.html.md +169 -0
- data/docs/source/release-notes/workarea-3-4-7.html.md +162 -0
- data/docs/source/release-notes/workarea-3-4-8.html.md +95 -0
- data/docs/source/release-notes/workarea-3-4-9.html.md +135 -0
- data/docs/source/search.html.erb +34 -0
- data/docs/source/shared/_header.erb +61 -0
- data/docs/source/shared/_svgs.erb +17 -0
- data/docs/source/style_guide/index.html.erb +382 -0
- data/docs/source/stylesheets/_base.scss +125 -0
- data/docs/source/stylesheets/_components.scss +669 -0
- data/docs/source/stylesheets/_helpers.scss +10 -0
- data/docs/source/stylesheets/_opinions.scss +42 -0
- data/docs/source/stylesheets/_settings.scss +56 -0
- data/docs/source/stylesheets/_typography.scss +119 -0
- data/docs/source/stylesheets/site.css.scss +14 -0
- data/docs/source/stylesheets/vendor/_avalanche.scss +328 -0
- data/docs/source/stylesheets/vendor/_normalize.scss +341 -0
- data/docs/source/stylesheets/vendor/highlight/_tomorrow_night_blue.scss +75 -0
- data/docs/source/upgrade-guides.html.md +18 -0
- data/docs/source/upgrade-guides/workarea-3-4-0.html.md +152 -0
- data/docs/workarea_renderer.rb +8 -0
- data/docs/yarn.lock +2522 -0
- metadata +669 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
---
|
2
|
+
title: Themes Overview
|
3
|
+
excerpt: A Workarea theme is a specialized type of Workarea plugin. Themes specifically customize the appearance of the Workarea platform.
|
4
|
+
---
|
5
|
+
|
6
|
+
# Themes Overview
|
7
|
+
|
8
|
+
A Workarea theme is a specialized type of Workarea plugin. Themes specifically
|
9
|
+
customize the appearance of the Workarea platform. Themes allow for re-use of
|
10
|
+
storefront code, helping to jump-start front-end work for an implementation. A
|
11
|
+
theme may be used as a starting point for front-end development, or as a
|
12
|
+
complete storefront UI.
|
13
|
+
|
14
|
+
## Installing a theme in a Workarea application
|
15
|
+
|
16
|
+
Installing a theme in a Workarea application is like installing any other plugin.
|
17
|
+
Add the theme's gem to your application's gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'workarea-your_theme_name'
|
21
|
+
```
|
22
|
+
|
23
|
+
Then run `bundle install` and restart your application.
|
24
|
+
|
25
|
+
Some themes may need extra configuration. Check the README.md for the theme you
|
26
|
+
are using to see if any further configuration is necessary.
|
27
|
+
|
28
|
+
## Configuring a theme
|
29
|
+
|
30
|
+
All themes include a `config/initializer/theme.rb` file, which provides options
|
31
|
+
for configuration within the Workarea application.
|
32
|
+
Typically themes allow configuration for color schemes and font families.
|
33
|
+
|
34
|
+
An example of a Workarea theme configuration file:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
Workarea.configure do |config|
|
38
|
+
config.theme = {
|
39
|
+
color_schemes: ["one", "workarea", "midnight"],
|
40
|
+
color_scheme: "one",
|
41
|
+
font_stacks: {
|
42
|
+
roboto: '"Roboto", "HelveticaNeue", "Helvetica Neue", sans-serif',
|
43
|
+
lora: '"Lora", "Times New Roman", "Georgia", serif',
|
44
|
+
hind: '"Hind", Helvetica, Arial, sans-serif',
|
45
|
+
source_serif_pro: '"Source Serif Pro", "Times New Roman", Georgia, serif',
|
46
|
+
muli: '"Muli", Helvetica, Arial, sans-serif',
|
47
|
+
playfair_display: '"Playfair Display", "Times New Roman", Georgia, serif'
|
48
|
+
},
|
49
|
+
primary_font_family: "roboto",
|
50
|
+
secondary_font_family: "lora"
|
51
|
+
}
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
## Approaches to using a theme
|
56
|
+
|
57
|
+
There are 2 ways to use a Workarea theme.
|
58
|
+
|
59
|
+
1. Installing the theme as a gem in your application, similar to other plugins.
|
60
|
+
2. As a development tool by running the `starter_store` generator to import a
|
61
|
+
theme's files to your application.
|
62
|
+
|
63
|
+
### Gem vs. Development tool approach
|
64
|
+
|
65
|
+
Using a Workarea theme as a gem is the quickest way to apply a theme to a Workarea
|
66
|
+
application. The benefit of running a theme as a gem is getting bug-fixes and upgrades
|
67
|
+
from the theme in patch releases.
|
68
|
+
|
69
|
+
Using a theme as a development tool is an alternative way to use a Workarea theme.
|
70
|
+
Rather than running the theme as a plugin; all of the theme's files, dependencies,
|
71
|
+
and configurations are copied in to your application. This is done by running the
|
72
|
+
`starter_store` generator from your host application. This approach allows greater
|
73
|
+
flexibility in development, however it removes you from the direct upgrade path
|
74
|
+
for the theme. This means you will need to use the Workarea upgrade tool to apply
|
75
|
+
patches and upgrades as they are released for the theme.
|
76
|
+
|
77
|
+
The development tool approach is most useful if:
|
78
|
+
|
79
|
+
- You plan to customize the application heavily using the theme as a starting point.
|
80
|
+
- You need to remove a dependency of the theme.
|
81
|
+
- For example a theme may depend on workarea-reviews, but your implementation uses
|
82
|
+
a 3rd party review system. The only way to use a theme and remove one of its
|
83
|
+
dependencies is by installing the theme using the `starter_store` generator.
|
84
|
+
|
85
|
+
### Using a theme as a development tool
|
86
|
+
|
87
|
+
To prepare for installing a theme as a starter store you must add the theme to
|
88
|
+
your application gemfile and run `db:seed`. Once you have the theme running in your
|
89
|
+
application run the `starter_store` generator
|
90
|
+
|
91
|
+
```bash
|
92
|
+
bundle exec rails g workarea:starter_store
|
93
|
+
```
|
94
|
+
|
95
|
+
During the execution of this generator you will be prompted to make decisions
|
96
|
+
re. overriding existing files in your application. Use the Ynaqdh interface
|
97
|
+
to make decisions on a per-file basis. I have found that 'n' is typically the
|
98
|
+
preferable choice, with the exception of locales/en.yml
|
99
|
+
|
100
|
+
After the generator is run you should:
|
101
|
+
|
102
|
+
1. Run bundle install.
|
103
|
+
2. Confirm that your application is running and is styled as expected.
|
104
|
+
3. Remove the now commented-out theme from your gemfile.
|
105
|
+
4. Run the full test suite and ensure nothing is failing.
|
106
|
+
5. Commit your changes and open a pull-request.
|
107
|
+
|
108
|
+
#### Appended files
|
109
|
+
|
110
|
+
Appends, and append removals will still be handled via the imported appends.rb
|
111
|
+
initializer. It is recommended that you review the contents of this file and
|
112
|
+
update your host application where appropriate to include these appended files
|
113
|
+
as normal, removing them from appends.rb. This is especially advisable for Sass
|
114
|
+
and JS assets which can easily be added to your application manifests.
|
115
|
+
|
116
|
+
## Upgrading a themed application
|
117
|
+
|
118
|
+
If you have installed your theme using the `starter_store` generator the upgrade
|
119
|
+
path is the same as any other application. The
|
120
|
+
[Workarea upgrade tool](https://stash.tools.weblinc.com/projects/WL/repos/workarea-upgrade/browse)
|
121
|
+
will be very helpful when upgrading your application.
|
122
|
+
|
123
|
+
If your application is using a theme as a plugin you should follow these steps:
|
124
|
+
|
125
|
+
1. Check whether your theme has already been upgraded for compatibility with the
|
126
|
+
version of Workarea you are upgrading to.
|
127
|
+
- If the theme is not yet upgraded contact the theme developer and find out
|
128
|
+
when that will be complete. It is not recommended to upgrade your application
|
129
|
+
until the theme is ready.
|
130
|
+
2. Upgrade Workarea and all other plugins first, use the Workarea upgrade tool to
|
131
|
+
create a diff and make the necessary changes in your application.
|
132
|
+
3. Update the theme's version in your gemfile to use the latest version that is
|
133
|
+
compatible with your new Workarea version.
|
134
|
+
4. Run the upgrade tool against your theme to see if there are further changes
|
135
|
+
that need to be made to files that have been overridden in your application.
|
136
|
+
|
137
|
+
Once all necessary changes highlighted in the diff have been made, tests are
|
138
|
+
passing, and your PR has been accepted, your application is upgraded and should
|
139
|
+
be sent to QA for testing. Drink a beer, you've earned it!
|
140
|
+
|
141
|
+
## Multisite support for themes
|
142
|
+
|
143
|
+
Multisite applications are able to use Workarea themes without many changes to
|
144
|
+
the normal multi-site development workflow.
|
145
|
+
|
146
|
+
Configuration options set in theme.rb should be applied to each instance of
|
147
|
+
`Workarea::Site`, this allows you to easily apply different color schemes and
|
148
|
+
fonts to each site with minimal effort. Skinning a new site for a themed
|
149
|
+
multi-site app should be relatively simple!
|
150
|
+
|
151
|
+
It is not possible to use more than 1 theme per application. This means you cannot
|
152
|
+
use different themes for different sites within a multi-site application. All sites
|
153
|
+
must be based on the same theme, but each can customize away from the theme as necessary.
|
154
|
+
Follow the [instructions for working with multi-site applications](https://stash.tools.weblinc.com/projects/WL/repos/workarea-multi-site/browse)
|
155
|
+
for more information about developing multi-site applications.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
title: Translate Administrable Content
|
3
|
+
excerpt: Many fields available through the Admin UI can be localized. These fields are indicated by a localization icon.
|
4
|
+
---
|
5
|
+
|
6
|
+
# Translate Administrable Content
|
7
|
+
|
8
|
+
Many fields available through the Admin UI can be localized. These fields are indicated by a localization icon.
|
9
|
+
|
10
|
+
To set the value of these fields for a different locale, use the locale switcher at the top of the Admin screen.
|
11
|
+
|
12
|
+
Translations of administrable content are stored with [Mongoid-localized fields](https://docs.mongodb.com/mongoid/6.4/tutorials/mongoid-documents/#localized-fields).
|
13
|
+
|
14
|
+
**Note:** This functionality will appear only if more than one locale is configured on the `I18n` module.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
title: Translate JavaScript Content
|
3
|
+
excerpt: Translate text within JavaScript templates and modules using the I18n.t() method provided by i18n-js (bundled with Workarea).
|
4
|
+
---
|
5
|
+
|
6
|
+
# Translate JavaScript Content
|
7
|
+
|
8
|
+
Translate text within JavaScript [templates](javascript-templates.html) and [modules](javascript-modules.html) using the `I18n.t()` method provided by [i18n-js](https://github.com/fnando/i18n-js) (bundled with Workarea).
|
9
|
+
|
10
|
+
The library provides access to the translations in `config/locales`, the same locale files used for [localization of static text in Ruby code](translate-or-customize-static-content.html).
|
11
|
+
|
12
|
+
workarea-storefront/app/assets/javascripts/workarea/storefront/templates/log\_out\_link.jst.ejs:
|
13
|
+
|
14
|
+
```
|
15
|
+
<span> (<a href="<%= WORKAREA.routes.storefront.logoutPath() %>" data-analytics="{'event':'logout', 'domEvent':'click'}" ><%= I18n.t('workarea.storefront.users.logout') %></a>)</span>
|
16
|
+
```
|
@@ -0,0 +1,29 @@
|
|
1
|
+
---
|
2
|
+
title: Translate or Customize Message Content
|
3
|
+
excerpt: Message text is translated/customized in the same manner as all static text.
|
4
|
+
---
|
5
|
+
|
6
|
+
# Translate or Customize Message Content
|
7
|
+
|
8
|
+
Message text is translated/customized in the same manner as all [static text](translate-or-customize-static-content.html).
|
9
|
+
|
10
|
+
To customize or translate message text, create a local file in your app for each locale (if you haven't already) and edit the values for the keys that correspond to the messages you'd like to change.
|
11
|
+
|
12
|
+
your\_app/config/locales/en.yml :
|
13
|
+
|
14
|
+
```
|
15
|
+
en:
|
16
|
+
workarea:
|
17
|
+
storefront:
|
18
|
+
# ...
|
19
|
+
flash_messages:
|
20
|
+
error: Error
|
21
|
+
no_search_query: You must enter a search term.
|
22
|
+
no_matching_order: We could not find a matching order. Please try again.
|
23
|
+
email_signed_up: Your email address has been added to our list.
|
24
|
+
email_signup_error: The provided email address was invalid.
|
25
|
+
contact_message_sent: Your message has been sent.
|
26
|
+
share_message_sent: Thanks for sharing! An email has been sent to %{recipient}.
|
27
|
+
# ...
|
28
|
+
# ...
|
29
|
+
```
|
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
title: Translate or Customize Static Content
|
3
|
+
excerpt: All static, user-facing text in Workarea UIs (including messages and emails) is translated through Rails' t helper. Use of this helper is explained in the Rails Internationalization (I18n) API guide.
|
4
|
+
---
|
5
|
+
|
6
|
+
# Translate or Customize Static Content
|
7
|
+
|
8
|
+
All static, user-facing text in Workarea UIs (including messages and emails) is translated through Rails' `t` helper. Use of this helper is explained in the [Rails Internationalization (I18n) API guide](http://guides.rubyonrails.org/i18n.html).
|
9
|
+
|
10
|
+
Translations are loaded from Yaml files in `config/locales` within your app.
|
11
|
+
|
12
|
+
workarea-storefront/app/controllers/workarea/storefront/cart\_items\_controller.rb:
|
13
|
+
|
14
|
+
```
|
15
|
+
# ...
|
16
|
+
|
17
|
+
flash[:success] = t('workarea.storefront.flash_messages.cart_item_added')
|
18
|
+
|
19
|
+
# ...
|
20
|
+
|
21
|
+
flash[:error] = t('workarea.storefront.flash_messages.cart_item_error')
|
22
|
+
|
23
|
+
# ...
|
24
|
+
```
|
25
|
+
|
26
|
+
workarea-storefront/app/views/layouts/workarea/storefront/application.html.haml:
|
27
|
+
|
28
|
+
```
|
29
|
+
.value= email_field_tag :email, nil, id: 'footer_email_signup_field', class: 'text-box', placeholder: t('workarea.storefront.forms.email_placeholder'), title: t('workarea.storefront.users.email'), required: true
|
30
|
+
```
|
@@ -0,0 +1,108 @@
|
|
1
|
+
---
|
2
|
+
title: Use an Existing Workarea App
|
3
|
+
excerpt: A quick guide to set up a Workarea application.
|
4
|
+
---
|
5
|
+
|
6
|
+
# Use an Existing Workarea Application
|
7
|
+
|
8
|
+
This is a quick start guide for getting started on an existing Workarea application. If you're a developer who has a new ticket on an app you just cloned for the first time, this is for you. These steps were written with the following assumptions:
|
9
|
+
|
10
|
+
* You have Docker Desktop installed. See [https://www.docker.com/products/docker-desktop](https://www.docker.com/products/docker-desktop) to download.
|
11
|
+
* You have Ruby >= 2.4.0, < 2.7.0 installed. See [https://github.com/rbenv/rbenv#installation](https://github.com/rbenv/rbenv#installation) for instructions.
|
12
|
+
* You have NodeJS installed. We recommend using [Homebrew](https://brew.sh).
|
13
|
+
* You have ImageMagick installed. We recommend using [Homebrew](https://brew.sh).
|
14
|
+
* You have the repository cloned locally.
|
15
|
+
|
16
|
+
For more details, see [Prerequisites and Dependencies](prerequisites-and-dependencies.html).
|
17
|
+
|
18
|
+
## Setup an Existing Workarea Application
|
19
|
+
|
20
|
+
Follow the steps below to quickly set up an existing Workarea application.
|
21
|
+
|
22
|
+
### 1. Check the README
|
23
|
+
|
24
|
+
Lots of projects put helpful startup info in their README file. Give it a look and see if there's anything you need to know there. We'll continue this guide assuming nothing relevant or more specific is in there.
|
25
|
+
|
26
|
+
```bash
|
27
|
+
$ cat README.md
|
28
|
+
```
|
29
|
+
|
30
|
+
### 2. Bundle to install gem dependencies
|
31
|
+
|
32
|
+
Workarea (and all Rails apps) depend on lots of gems, install the bundle to resolve and install the gems:
|
33
|
+
|
34
|
+
```bash
|
35
|
+
$ bundle install
|
36
|
+
```
|
37
|
+
|
38
|
+
### 3. Start Workarea service dependencies
|
39
|
+
|
40
|
+
Workarea relies on a few databases, so there's a task that will start them in Docker containers.
|
41
|
+
Start Workarea dependencies:
|
42
|
+
|
43
|
+
```bash
|
44
|
+
$ bin/rails workarea:services:up
|
45
|
+
```
|
46
|
+
|
47
|
+
### 5. Seed the database
|
48
|
+
|
49
|
+
To do anything useful with Workarea, you'll want some sample data in your database.
|
50
|
+
The install generator run in step 3 will add Workarea seeds to your `db/seeds.rb` file,
|
51
|
+
so running Rails seeds will add sample Workarea data.
|
52
|
+
|
53
|
+
```bash
|
54
|
+
$ bin/rails db:seeds
|
55
|
+
```
|
56
|
+
|
57
|
+
For more details on working with seed data, see [Seeds](seeds.html).
|
58
|
+
|
59
|
+
### 6. Start the Rails server
|
60
|
+
|
61
|
+
Use the conventional Rails command for starting up the Puma server:
|
62
|
+
|
63
|
+
```bash
|
64
|
+
$ bin/rails server
|
65
|
+
```
|
66
|
+
|
67
|
+
### 7. Open the Application in a Browser
|
68
|
+
|
69
|
+
Your Workarea application is ready! Open a browser, and check out `http://localhost:3000`.
|
70
|
+
|
71
|
+
|
72
|
+
## Addendum
|
73
|
+
|
74
|
+
### 8. Run Tests
|
75
|
+
|
76
|
+
Running tests is a regular part of developing on Workarea. Check out the list of Rails tasks Workarea provides for testing Workarea:
|
77
|
+
|
78
|
+
```bash
|
79
|
+
$ bin/rails -T workarea:test
|
80
|
+
rails workarea:test # Run workarea tests (with decorators)
|
81
|
+
rails workarea:test:admin # Run workarea admin tests (with decorators)
|
82
|
+
rails workarea:test:app # Run all app specific tests
|
83
|
+
rails workarea:test:core # Run workarea/core tests (with decorators)
|
84
|
+
rails workarea:test:decorated # Run decorated tests
|
85
|
+
rails workarea:test:performance # Run workarea performance tests (with decorators)
|
86
|
+
rails workarea:test:plugins # Run all installed workarea plugin tests (with decorators)
|
87
|
+
rails workarea:test:storefront # Run workarea storefront tests (with decorators)
|
88
|
+
```
|
89
|
+
|
90
|
+
This will run the Workarea test suite (with any [decoration to the tests](decoration.html) this app adds):
|
91
|
+
|
92
|
+
```bash
|
93
|
+
$ bin/rails workarea:test
|
94
|
+
```
|
95
|
+
|
96
|
+
For more details on Workarea's testing functionality, see [Testing](testing.html).
|
97
|
+
|
98
|
+
### 9. Stop the Services
|
99
|
+
|
100
|
+
After developing and testing, you may want to stop the services to conserve resources on your machine.
|
101
|
+
|
102
|
+
Run the command to stop the services Workarea has started for you:
|
103
|
+
|
104
|
+
```bash
|
105
|
+
$ bin/rails workarea:services:down
|
106
|
+
```
|
107
|
+
|
108
|
+
Note that the volumes these containers used will still be available, and you won't have to seed again the next time you start them.
|
@@ -0,0 +1,509 @@
|
|
1
|
+
---
|
2
|
+
title: View Models
|
3
|
+
excerpt: Workarea adds a view model layer to the standard controller/model/view/helper architecture provided by Rails. View models are concerned with database reads and logic related to those reads. Each view model instance "wraps" a model instance in order to
|
4
|
+
---
|
5
|
+
|
6
|
+
# View Models
|
7
|
+
|
8
|
+
Workarea adds a <dfn>view model</dfn> layer to the standard controller/model/view/helper architecture provided by Rails. View models are concerned with database reads and logic related to those reads. Each view model instance "wraps" a model instance in order to modify and augment it before presenting it within a particular UI, such as the Admin or Store Front.
|
9
|
+
|
10
|
+
Review the Storefront's categories controller, shown below in its entirety:
|
11
|
+
|
12
|
+
workarea-storefront/app/controllers/workarea/storefront/categories\_controller.rb:
|
13
|
+
|
14
|
+
```
|
15
|
+
module Workarea
|
16
|
+
class Storefront::CategoriesController < Storefront::ApplicationController
|
17
|
+
before_filter :cache_page
|
18
|
+
|
19
|
+
def show
|
20
|
+
model = Catalog::Category.find_by_slug(params[:id])
|
21
|
+
@category = Storefront::CategoryViewModel.new(model, view_model_options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
The `show` method in this controller resembles the simple controller actions shown in beginning Rails tutorials. Controller actions in such tutorials use the params to get a model instance, store it in an instance variable that will be accessible in the view, and finish by implicitly rendering the view based on naming convention. The Workarea controller above is identical, with one exception. The model is "wrapped" with a view model, so `@category` refers to a view model instance rather than a model instance.
|
28
|
+
|
29
|
+
In the view, the view model instance provides the single interface for all the data and logic needed to present the model within this context. In the view excerpt below, you can see how the view model instance stored in `@category` provides the data and logic needed to present a category within the Store Front.
|
30
|
+
|
31
|
+
workarea-storefront/app/views/workarea/storefront/categories/show.html.haml:
|
32
|
+
|
33
|
+
```
|
34
|
+
/ ...
|
35
|
+
|
36
|
+
- cache @category.cache_key, expires_in: 1.hour do
|
37
|
+
.view{ data: { analytics: category_view_analytics_data(@category).to_json } }
|
38
|
+
|
39
|
+
%h1= @category.name
|
40
|
+
|
41
|
+
- if @category.content_for(:area_1).present?
|
42
|
+
= render_content_blocks(@category.content_for(:area_1))
|
43
|
+
|
44
|
+
- unless @category.products.any?
|
45
|
+
%p
|
46
|
+
= t('workarea.storefront.products.none_found')
|
47
|
+
- if @category.has_filters?
|
48
|
+
= link_to t('workarea.storefront.products.reset_filters'), category_path(@category)
|
49
|
+
/ ...
|
50
|
+
```
|
51
|
+
|
52
|
+
## Philosophy
|
53
|
+
|
54
|
+
Before looking more closely at Workarea's view model implementation, I'd like to explain _why_ view models are used within the platform.
|
55
|
+
|
56
|
+
Within Workarea, view models are used to:
|
57
|
+
|
58
|
+
- read across bounded contexts,
|
59
|
+
- present a model within a UI,
|
60
|
+
- keep controllers lean,
|
61
|
+
- and provide a single interface to the view, reducing view logic.
|
62
|
+
|
63
|
+
Let's look at each of these in turn.
|
64
|
+
|
65
|
+
### Read Across Bounded Contexts
|
66
|
+
|
67
|
+
As explained in the [Domain Modeling](domain-modeling.html) guide, Workarea's models are organized into bounded contexts. The models within each bounded context address a particular problem set, and they do not cross their boundaries into other contexts.
|
68
|
+
|
69
|
+
For example, orders, users, payments, shipments, and fulfullments are sufficiently complicated that each is encapsulated into its own context. However, presenting an order to an end user requires reading from all of these contexts. **View models may read across bounded contexts**.
|
70
|
+
|
71
|
+
Take a look at the following excerpt from the Store Front's order view model:
|
72
|
+
|
73
|
+
workarea-storefront/app/view\_models/workarea/storefront/order\_view\_model.rb:
|
74
|
+
|
75
|
+
```
|
76
|
+
module Workarea
|
77
|
+
module Storefront
|
78
|
+
class OrderViewModel < ApplicationViewModel
|
79
|
+
# ...
|
80
|
+
|
81
|
+
delegate :tenders, :credit_card, :credit_card?,
|
82
|
+
:store_credit?, :store_credit,
|
83
|
+
to: :payment
|
84
|
+
|
85
|
+
# ...
|
86
|
+
|
87
|
+
delegate :shipping_method, to: :shipment, allow_nil: true
|
88
|
+
|
89
|
+
def user
|
90
|
+
@user ||= User.find(model.user_id) if model.user_id.present?
|
91
|
+
end
|
92
|
+
|
93
|
+
# ...
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def payment
|
98
|
+
@payment ||= Payment.find_or_initialize_by(id: number)
|
99
|
+
end
|
100
|
+
|
101
|
+
def shipment
|
102
|
+
@shipment ||= Shipping::Shipment.find_by_number(number)
|
103
|
+
end
|
104
|
+
|
105
|
+
def fulfillment
|
106
|
+
@fulfillment ||= Fulfillment.find_or_initialize_by(id: number)
|
107
|
+
end
|
108
|
+
|
109
|
+
# ...
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
The `user`, `payment`, `shipment`, and `fulfillment` methods clearly reach across domain boundaries to retrieve the necessary data from models within other contexts. You can also see other methods being delegated to the payment and the shipment. Data reads that cross domain boundaries in this fashion are unique to view models. In this way, **view models keep models decoupled**.
|
116
|
+
|
117
|
+
### Present a Model Within a UI
|
118
|
+
|
119
|
+
The view model above is responsible for presenting an order in the Store Front. In order to do this, the view model modifies and augments the state and behavior of the order model instance that it wraps. It does so to format the data for display within the Store Front.
|
120
|
+
|
121
|
+
For example, when presenting an order in the Store Front, the store credit amount must always be available as a non-`nil` value. The `store_credit_amount` method in the Store Front order view model takes care of this:
|
122
|
+
|
123
|
+
workarea-storefront/app/view\_models/workarea/storefront/order\_view\_model.rb:
|
124
|
+
|
125
|
+
```
|
126
|
+
module Workarea
|
127
|
+
module Storefront
|
128
|
+
class OrderViewModel < ApplicationViewModel
|
129
|
+
# ...
|
130
|
+
|
131
|
+
def store_credit_amount
|
132
|
+
if store_credit.present?
|
133
|
+
store_credit.amount
|
134
|
+
else
|
135
|
+
0.to_m
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# ...
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
Additionally, to display a full name, the Storefront _and_ Admin order view models both implement `full_name`:
|
146
|
+
|
147
|
+
workarea-admin/app/view\_models/workarea/admin/order\_view\_model.rb:
|
148
|
+
|
149
|
+
```
|
150
|
+
module Workarea
|
151
|
+
module Admin
|
152
|
+
class OrderViewModel < ApplicationViewModel
|
153
|
+
# ...
|
154
|
+
|
155
|
+
def full_name
|
156
|
+
if billing_address.present?
|
157
|
+
"#{billing_address.first_name} #{billing_address.last_name}"
|
158
|
+
elsif shipping_address.present?
|
159
|
+
"#{shipping_address.first_name} #{shipping_address.last_name}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# ...
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
The store credit and full name examples demonstrate methods that are concerned with the _presentation_ of data. **View models keep presentation logic out of models** , which are concerned with business logic rather than presentation.
|
170
|
+
|
171
|
+
### Keep Controllers Lean
|
172
|
+
|
173
|
+
As with presenting an order, presenting a product requires complex data reads across several contexts. Despite this, the Store Front's products controller is lean and easy to read:
|
174
|
+
|
175
|
+
workarea-storefront/app/controllers/workarea/storefront/products\_controller.rb:
|
176
|
+
|
177
|
+
```
|
178
|
+
module Workarea
|
179
|
+
class Storefront::ProductsController < Storefront::ApplicationController
|
180
|
+
before_filter :cache_page
|
181
|
+
|
182
|
+
def show
|
183
|
+
model = Catalog::Product.find_for_display!(params[:id])
|
184
|
+
|
185
|
+
@product = Storefront::ProductViewModel.wrap(
|
186
|
+
model,
|
187
|
+
view_model_options
|
188
|
+
)
|
189
|
+
|
190
|
+
if request.xhr?
|
191
|
+
render 'quickview'
|
192
|
+
else
|
193
|
+
render 'show'
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
```
|
199
|
+
|
200
|
+
The read logic exists in the view model that wraps the model instance. **View models keep complicated read logic out of controllers**.
|
201
|
+
|
202
|
+
### Provide a Single Interface to the View
|
203
|
+
|
204
|
+
The products controller above contains a familiar pattern: find the relevant model instance, wrap it in a view model, assign it to an instance variable, then render the view. The `@product` instance variable encapsulates all the data and logic needed to present the model, all wrapped up nicely for the view.
|
205
|
+
|
206
|
+
By providing this single interface to the view, **view models reduce logic in views**. Keeping this logic out the view makes the logic easier to test and makes the view easier to read.
|
207
|
+
|
208
|
+
Having explained the philosophy of view models, let's move on to the most important implementation details, starting with initialization.
|
209
|
+
|
210
|
+
## Initialization/Wrapping
|
211
|
+
|
212
|
+
View model instances are initialized with a model instance and an optional hash of additional data. Each view model instance therefore "wraps" a model instance.
|
213
|
+
|
214
|
+
The Store Front pages controller, for example, includes an instance of `PageViewModel` wrapping an instance of `Page` and an instance of `ContentViewModel` wrapping an instance of `Content`.
|
215
|
+
|
216
|
+
workarea-storefront/app/controllers/workarea/storefront/pages\_controller.rb:
|
217
|
+
|
218
|
+
```
|
219
|
+
module Workarea
|
220
|
+
class Storefront::PagesController < Storefront::ApplicationController
|
221
|
+
before_filter :cache_page
|
222
|
+
|
223
|
+
def show
|
224
|
+
model = Content::Page.find_by_slug(params[:id])
|
225
|
+
@page = Storefront::PageViewModel.new(model, view_model_options)
|
226
|
+
end
|
227
|
+
|
228
|
+
def home_page
|
229
|
+
@page = Storefront::ContentViewModel.new(
|
230
|
+
Content.for('home_page'),
|
231
|
+
view_model_options
|
232
|
+
)
|
233
|
+
end
|
234
|
+
|
235
|
+
def robots; end
|
236
|
+
def accessibility; end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
241
|
+
In both cases, `view_model_options` is passed as the additional data hash. This method is mixed into all Store Front controllers and returns the params from the controller with the current user merged in.
|
242
|
+
|
243
|
+
### Model Types
|
244
|
+
|
245
|
+
The model being wrapped does not need to be a formal model (a Mongoid document), as demonstrated by the Admin searches controller:
|
246
|
+
|
247
|
+
workarea-admin/app/controllers/workarea/admin/searches\_controller.rb:
|
248
|
+
|
249
|
+
```
|
250
|
+
module Workarea
|
251
|
+
module Admin
|
252
|
+
class SearchesController < Admin::ApplicationController
|
253
|
+
def show
|
254
|
+
search = Search::Queries::AdminSearch.new(params)
|
255
|
+
@search = SearchViewModel.new(search, params)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
In this example, the model is a search query object, and the params are passed as options.
|
263
|
+
|
264
|
+
### New vs Wrap
|
265
|
+
|
266
|
+
You may have noticed that a few of the examples above create a view model instance using the `wrap` method rather than `new`. In fact, **`wrap` is the preferred API to create a view model and should be used when creating new view model instances**. The next major version of Workarea will use `wrap` exclusively. Beyond improved semantics, `wrap` differs technically from `new` in that (1) it can create an array of view models from an enumerable and (2) it may return a specialized view model type.
|
267
|
+
|
268
|
+
### Wrapping an Enumerable
|
269
|
+
|
270
|
+
Passing an enumerable to `wrap` returns an array of view model instances. One such example is the `Storefront::DisplayContent` mixin:
|
271
|
+
|
272
|
+
workarea-storefront/app/view\_models/workarea/storefront/display\_content.rb:
|
273
|
+
|
274
|
+
```
|
275
|
+
module Workarea
|
276
|
+
module Storefront
|
277
|
+
module DisplayContent
|
278
|
+
# ...
|
279
|
+
|
280
|
+
def content_for(area)
|
281
|
+
return [] unless content.present?
|
282
|
+
blocks = content.blocks_for(area).select(&:active?)
|
283
|
+
ContentBlockViewModel.wrap(blocks, options)
|
284
|
+
end
|
285
|
+
|
286
|
+
# ...
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
```
|
291
|
+
|
292
|
+
In this example, `blocks` is an array of `Content::Block` models. The `content_for` method returns an array of `ContentBlockViewModel` instances.
|
293
|
+
|
294
|
+
### Specialization
|
295
|
+
|
296
|
+
Unlike `new`, `wrap` may return a view model instance of a different type. This occurs when `wrap` identifies a specialized view model type that is more appropriate. For example, `ContentBlockViewModel.wrap` will try to find a view model matching the specific block type, and `ProductViewModel.wrap` will try to find a view model matching the product's template type.
|
297
|
+
|
298
|
+
While most view models inherit directly from `ApplicationViewModel`, "specialized" view models inherit from another view model that in turn inherits from `ApplicationViewModel`, giving them the properties of both view models.
|
299
|
+
|
300
|
+
The excerpt below shows the Store Front's content block view model. Notice how the `locals` method returns a hash created from the model's data:
|
301
|
+
|
302
|
+
workarea-storefront/app/view\_models/workarea/storefront/content\_block\_view\_model.rb:
|
303
|
+
|
304
|
+
```
|
305
|
+
module Workarea
|
306
|
+
module Storefront
|
307
|
+
class ContentBlockViewModel < ApplicationViewModel
|
308
|
+
def self.wrap(model, options = {})
|
309
|
+
# ...
|
310
|
+
end
|
311
|
+
|
312
|
+
def partial
|
313
|
+
# ...
|
314
|
+
end
|
315
|
+
|
316
|
+
def locals
|
317
|
+
model.data.try(:symbolize_keys) || {}
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
```
|
323
|
+
|
324
|
+
The category summary view model "specializes" the content block view model, overwriting the `locals` method and adding additional methods needed to present a category summary content block within the Store Front:
|
325
|
+
|
326
|
+
workarea-storefront/app/view\_models/workarea/storefront/content\_blocks/category\_summary\_view\_model.rb:
|
327
|
+
|
328
|
+
```
|
329
|
+
module Workarea
|
330
|
+
module Storefront
|
331
|
+
module ContentBlocks
|
332
|
+
class CategorySummaryViewModel < ContentBlockViewModel
|
333
|
+
include ProductBrowsing
|
334
|
+
|
335
|
+
def locals
|
336
|
+
{ category: category, products: products }
|
337
|
+
end
|
338
|
+
|
339
|
+
def category
|
340
|
+
# ...
|
341
|
+
end
|
342
|
+
|
343
|
+
def products
|
344
|
+
# ...
|
345
|
+
end
|
346
|
+
|
347
|
+
# ...
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
```
|
353
|
+
|
354
|
+
### Wrap Example
|
355
|
+
|
356
|
+
In a newly created Workarea application, the home page content is composed of a variety of content blocks. The following example console session uses those blocks to demonstrate (1) wrapping an enumerable and (2) specialized view model types:
|
357
|
+
|
358
|
+
```
|
359
|
+
# Create an array of content block models
|
360
|
+
irb(main):001:0> home_page_blocks = Workarea::Content.for('home_page').blocks
|
361
|
+
|
362
|
+
# Wrap the array with ContentBlockViewModel
|
363
|
+
irb(main):002:0> view_models = Workarea::Storefront::ContentBlockViewModel.wrap(home_page_blocks)
|
364
|
+
|
365
|
+
# Confirm the return value is an array
|
366
|
+
irb(main):003:0> view_models.class
|
367
|
+
=> Array
|
368
|
+
|
369
|
+
# Print the type of each value in the array
|
370
|
+
# Some are ContentBlockViewModel
|
371
|
+
# Others are specialized types
|
372
|
+
irb(main):004:0> puts view_models.map(&:class)
|
373
|
+
Workarea::Storefront::ContentBlocks::HeroViewModel
|
374
|
+
Workarea::Storefront::ContentBlocks::BannerViewModel
|
375
|
+
Workarea::Storefront::ContentBlockViewModel
|
376
|
+
Workarea::Storefront::ContentBlocks::VideoViewModel
|
377
|
+
Workarea::Storefront::ContentBlocks::CategorySummaryViewModel
|
378
|
+
Workarea::Storefront::ContentBlockViewModel
|
379
|
+
Workarea::Storefront::ContentBlockViewModel
|
380
|
+
```
|
381
|
+
|
382
|
+
## Method Delegation
|
383
|
+
|
384
|
+
It's important to understand the method delegation that occurs within view models. View models may replace existing methods on the models they wrap, as well as add additional methods to those models. **View model instances delegate to the model for all methods they have not replaced or added**.
|
385
|
+
|
386
|
+
View models also provide a `model` method to access the wrapped model (and its methods) directly.
|
387
|
+
|
388
|
+
The following example console session demonstrates these concepts:
|
389
|
+
|
390
|
+
```
|
391
|
+
# Get a product model instance
|
392
|
+
irb(main):027:0> product = Workarea::Catalog::Product.first
|
393
|
+
|
394
|
+
# Create a view model instance from that model
|
395
|
+
irb(main):028:0> product_view_model = Workarea::Storefront::ProductViewModel.wrap(product)
|
396
|
+
|
397
|
+
# Access the original model's `images` method from the view model
|
398
|
+
# It returns an array
|
399
|
+
irb(main):031:0> product_view_model.model.images.class
|
400
|
+
=> Array
|
401
|
+
|
402
|
+
# Now call the same method directly on the view model instance
|
403
|
+
# The view model redefines that method, returning an `ImageCollection`
|
404
|
+
irb(main):030:0> product_view_model.images.class
|
405
|
+
=> Workarea::Storefront::ProductViewModel::ImageCollection
|
406
|
+
|
407
|
+
# Try to call `primary_image` on the original model
|
408
|
+
# The method doesn't exist
|
409
|
+
irb(main):034:0> product.primary_image
|
410
|
+
=> # NoMethodError: undefined method `primary_image' for #<Workarea::Catalog::Product:0x007f729c69a738>
|
411
|
+
|
412
|
+
# However, it does exist on the view model
|
413
|
+
# The view model has augmented the original model
|
414
|
+
irb(main):035:0> product_view_model.primary_image
|
415
|
+
=> #<Workarea::Catalog::ProductPlaceholderImage _id: 575b276d7765623aca000091, created_at: 2016-06-10 20:47:41 UTC, updated_at: 2016-06-10 20:47:41 UTC, image_uid: "2016/06/10/5eggsd9wcc_product_placeholder.jpg">
|
416
|
+
|
417
|
+
# The view model does not define a `categories` method
|
418
|
+
irb(main):037:0> product_view_model.methods.include?(:categories)
|
419
|
+
=> false
|
420
|
+
|
421
|
+
# However, calling the method on the view model returns a `Mongoid::Criteria`
|
422
|
+
# The view model delegates to the model, which defines this method
|
423
|
+
irb(main):038:0> product_view_model.categories
|
424
|
+
=> #<Mongoid::Criteria
|
425
|
+
selector: {"deleted_at"=>nil, "product_ids"=>{"$in"=>["2F2DA168D8"]}}
|
426
|
+
options: {}
|
427
|
+
class: Workarea::Catalog::Category
|
428
|
+
embedded: false>
|
429
|
+
```
|
430
|
+
|
431
|
+
## View Model Interface Diagram
|
432
|
+
|
433
|
+
The diagram that follows brings together many of the ideas from above:
|
434
|
+
|
435
|
+
* As needed, the view model initializes and queries a variety of objects to extend the interface of the original "wrapped" model
|
436
|
+
* The view model instance, which provides this extended interface, is bound to an instance variable
|
437
|
+
* The view "receives" this instance variable, from which it can query all data necessary to render the page or fragment
|
438
|
+
|
439
|
+

|
440
|
+
|
441
|
+
|
442
|
+
## View Models vs Helpers
|
443
|
+
|
444
|
+
Rails includes the concept of <dfn>helpers</dfn>—classes whose methods are accessible in views—for the purpose of extracting view logic into testable classes. Helpers overlap conceptually with view models, and Workarea makes use of both object types. In general, Workarea applications prefer view models over helpers, largely because view models are easier to decorate and test. However, Workarea does make use of helper methods, primarily for the following use cases:
|
445
|
+
|
446
|
+
- Methods concerned with constructing HTML strings, such as a list of class values for an HTML element.
|
447
|
+
- Methods that return re-usable snippets of HTML, such as `datetime_picker_tag`.
|
448
|
+
- Methods that depend on or compose Rails' own view helpers, such as `render_content_block`, shown below.
|
449
|
+
- Methods that depend on arguments received from the view.
|
450
|
+
|
451
|
+
The following examples demonstrate the typical usage of helper methods within a Workarea application. Notice that each method is concerned with constructing an HTML string, and some methods depend on helpers provided by Rails.
|
452
|
+
|
453
|
+
workarea-storefront/app/helpers/workarea/storefront/content\_helper.rb:
|
454
|
+
|
455
|
+
```
|
456
|
+
module Workarea
|
457
|
+
module Storefront
|
458
|
+
module ContentHelper
|
459
|
+
def render_content_block(block)
|
460
|
+
render(partial: block.partial, locals: block.locals)
|
461
|
+
end
|
462
|
+
|
463
|
+
def render_content_blocks(blocks)
|
464
|
+
blocks.inject('') do |result, block|
|
465
|
+
result << render_content_block(block)
|
466
|
+
result
|
467
|
+
end.html_safe
|
468
|
+
end
|
469
|
+
|
470
|
+
def hero_link_class(hero_link_style)
|
471
|
+
style = hero_link_style.parameterize
|
472
|
+
|
473
|
+
if style == 'button'
|
474
|
+
'button'
|
475
|
+
else
|
476
|
+
"hero-content-block __action-text hero-content-block__ action-text--#{style}"
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
```
|
483
|
+
|
484
|
+
workarea-storefront/app/helpers/workarea/storefront/credit\_cards\_helper.rb:
|
485
|
+
|
486
|
+
```
|
487
|
+
module Workarea
|
488
|
+
module Storefront
|
489
|
+
module CreditCardsHelper
|
490
|
+
def credit_card_issuer_icon(issuer)
|
491
|
+
content_tag(
|
492
|
+
:span,
|
493
|
+
issuer,
|
494
|
+
class: credit_card_issuer_icon_class(issuer),
|
495
|
+
title: issuer
|
496
|
+
)
|
497
|
+
end
|
498
|
+
|
499
|
+
def credit_card_issuer_icon_class(issuer)
|
500
|
+
if Workarea.config.credit_card_issuers.values.include?(issuer)
|
501
|
+
"payment-icon payment-icon--#{issuer.parameterize}"
|
502
|
+
else
|
503
|
+
'payment-icon'
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
```
|