Class: Oembed::ProductProvider
- Inherits:
-
Object
- Object
- Oembed::ProductProvider
- Defined in:
- app/services/oembed/product_provider.rb
Overview
Custom oEmbed-like provider for WarmlyYours Products
Returns rendered product HTML for embedding in Redactor editor
This allows products to be visually previewed in the editor while maintaining
the ability to edit which product is displayed via context menu.
The output includes:
- Magazine-style product card with image, title, SKU, price
- Locale-aware pricing (US/Canada)
- Add to cart button
- Product JSON-LD structured data for SEO
- Data attributes for editing in Redactor
Products are automatically refreshed by the overnight job to:
- Update prices when they change
- Hide products that become discontinued or unavailable
Usage:
provider = Oembed::ProductProvider.new
result = provider.get(sku: 'TRT512-KIT', locale: 'en-US')
result[:html] # => Rendered product HTML with embedded JSON-LD schema
Defined Under Namespace
Classes: ProductNotFoundError, ProductUnavailableError
Class Method Summary collapse
-
.curl_typographic_quotes(text) ⇒ String
Convert straight ASCII quotes to curly typographic quotes per the project's curly-quote convention (https://typographyforlawyers.com/straight-and-curly-quotes.html).
-
.sanitize_schema_text(text) ⇒ String?
Prepare description text for embedding in a JSON-LD
<script>tag.
Instance Method Summary collapse
-
#get(options = {}) ⇒ Hash
Get rendered product HTML.
-
#render_for_locale(sku, locale = 'en-US', include_schema: true) ⇒ String?
Render product embed HTML for the frontend (blog display) This is called during content refresh and normal rendering Handles locale-aware pricing and availability checks.
Class Method Details
.curl_typographic_quotes(text) ⇒ String
Convert straight ASCII quotes to curly typographic quotes per the
project's curly-quote convention
(https://typographyforlawyers.com/straight-and-curly-quotes.html).
Kept as a thin shim around Heatwave::TypographicQuotes.curl_plain
so existing call sites continue to work; new callers should use
the module method directly.
319 320 321 |
# File 'app/services/oembed/product_provider.rb', line 319 def self.curl_typographic_quotes(text) Heatwave::TypographicQuotes.curl_plain(text) end |
.sanitize_schema_text(text) ⇒ String?
Prepare description text for embedding in a JSON-LD <script> tag.
Two concerns:
- Word boundaries — replace block-level tags with spaces before
stripping so<p>foo</p><p>bar</p>becomesfoo bar, not
foobar. - Quote safety — convert straight ASCII quotes to curly typographic
quotes via Heatwave::TypographicQuotes.curl_plain. Even with
to_jsonescaping straight"as\", any tool that round-trips
the JSON-LD as HTML text (Redactor, Nokogiri-based refreshers, etc.)
can unescape\"back to", leaving invalid JSON inside the
script tag. Curly quotes are different code points entirely and
survive every round-trip intact.
Idempotent: re-running on already-sanitized text yields the same
output (curly quotes pass through, block tags are already gone).
298 299 300 301 302 303 304 305 |
# File 'app/services/oembed/product_provider.rb', line 298 def self.sanitize_schema_text(text) return text if text.blank? spaced = text.to_s.gsub(BLOCK_TAG_REGEX, ' ') sanitized = ActionView::Base.full_sanitizer.sanitize(spaced) decoded = CGI.unescapeHTML(sanitized).squish Heatwave::TypographicQuotes.curl_plain(decoded) end |
Instance Method Details
#get(options = {}) ⇒ Hash
Get rendered product HTML
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'app/services/oembed/product_provider.rb', line 39 def get( = {}) sku = [:sku].to_s.strip locale = [:locale].to_s.presence || 'en-US' = [:embedded_asset_uuid].presence raise ProductNotFoundError, 'No SKU provided' if sku.blank? # Try exact match first, then case-insensitive item = Item.find_by(sku: sku) || Item.where('UPPER(sku) = ?', sku.upcase).first raise ProductNotFoundError, "Product not found: #{sku}" if item.nil? # For CRM editor preview, show the product with locale-aware pricing # The frontend rendering will handle final availability checks build_response(item, sku, locale, embedded_asset_uuid: ) end |
#render_for_locale(sku, locale = 'en-US', include_schema: true) ⇒ String?
Render product embed HTML for the frontend (blog display)
This is called during content refresh and normal rendering
Handles locale-aware pricing and availability checks
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'app/services/oembed/product_provider.rb', line 63 def render_for_locale(sku, locale = 'en-US', include_schema: true) sku_stripped = sku.to_s.strip item = Item.find_by(sku: sku_stripped) || Item.where('UPPER(sku) = ?', sku_stripped.upcase).first return nil if item.nil? catalog_id = catalog_id_for_locale(locale) catalog_item = item.catalog_items.find_by(catalog_id: catalog_id) # Check availability - return nil if product shouldn't be displayed return nil unless product_available?(item, catalog_item) # Render the product card card_html = render_product_card(item, catalog_item, locale) return nil if card_html.nil? # Optionally include JSON-LD schema for SEO if include_schema schema_html = render_product_schema(item, catalog_item, locale) "#{card_html}#{schema_html}" else card_html end end |