Skip to content

Instantly share code, notes, and snippets.

@ismasan
Last active July 27, 2017 19:32
Show Gist options
  • Select an option

  • Save ismasan/eabc3d9c5505c1da062db4cc5fbb5a7f to your computer and use it in GitHub Desktop.

Select an option

Save ismasan/eabc3d9c5505c1da062db4cc5fbb5a7f to your computer and use it in GitHub Desktop.
Proof of concept for variants with associated headings
require 'ostruct'
require 'set'
require 'rspec'
# rspec variants.rb
#
RSpec.describe "grouping variants by headings, and using variant headings to filter products" do
before do
# Create shop
@shop = Shop.new
# Create product
@p1 = Product.new(
'P1',
attributes: {
'Altura' => '100cm'
},
# customize variant headings for this product
# the shop owner does this by editing the column names
# in a product's variant list, or similar
#
# opt1 (Color) | opt2 (Talla) | sku | stock
# --------------|--------------|------|--------
# rojo | 36 | abc | 10
# verde | 36 | bca | 11
#
variant_headings: {
opt1: 'Color',
opt2: 'Talla'
}
)
# add product to shop
@shop.products << @p1
# create a couple of variants
@v1 = OpenStruct.new(
opt1: "rojo",
opt2: 36,
sku: 'abc',
stock: 10
)
@v2 = OpenStruct.new(
opt1: "verde",
opt2: 40,
sku: 'bca',
stock: 11
)
# add variants to product
@p1.variants << @v1
@p1.variants << @v2
# Create another product
# This one has "Talla" as an attribute, not a variant
@p2 = Product.new(
'P2',
attributes: {
'Altura' => '110cm',
'Talla' => 'XL',
},
# customize variant headings for this product
variant_headings: {
opt1: 'Material',
opt2: 'Color',
}
)
@shop.products << @p2
# create a variant for second product
@v3 = OpenStruct.new(
opt1: 'cuero',
opt2: "rojo",
sku: 'abc',
stock: 1
)
@p2.variants << @v3
end
# this data structure is only useful internally
it "lists all unique variant/attribute headings in shop" do
list = @shop.searchable_attribute_names
expect(list).to eq %w(Color Talla Altura Material)
end
# for a Storefronts menu that lists all 'tallas' in the shop
it "lists all values for 'Talla' in shop" do
list = @shop.values_for('Talla')
expect(list).to eq [36, 40, 'XL']
end
# for a Storefronts menu that lists all 'materiales' in the shop
it "lists all values for 'Material' in shop" do
list = @shop.values_for('Material')
expect(list).to eq ["cuero"]
end
# for a Storefronts menu that lists all 'colors' in the shop
it "lists all values for 'Color' in shop" do
list = @shop.values_for('Color')
expect(list).to eq ["rojo", "verde"]
end
# The user select a Color from menu and filters products by it
it "filters products that have variants or attributes with heading 'Color' and value 'rojo'" do
products = @shop.filter_products('Color:rojo')
expect(products.map(&:name)).to eq %w(P1 P2)
end
it "filters products that have variants or attributes heading 'Color' and value 'verde'" do
products = @shop.filter_products('Color:verde')
expect(products.map(&:name)).to eq %w(P1)
end
it "filters products that have attribute heading 'Talla' and value 'XL'" do
products = @shop.filter_products('Talla:XL')
expect(products.map(&:name)).to eq %w(P2)
end
end
## Quick and ugly implementation. Here be dragons!!
class Shop
attr_reader :products
def initialize
@products = []
end
def filter_products(term)
key, value = term.split(':')
index.search key, value
end
def searchable_attribute_names
index.keys
end
def values_for(key)
index.keys_at key
end
private
# Material:
# cuero: [p1, p2]
# Color:
# rojo: [p1]
# verde: [p2]
def index
@index ||= build_index
end
def build_index
idx = Index.new
products.each do |pr|
# index variants
mappings = pr.variant_headings
pr.variants.each do |vr|
mappings.each do |k, v|
value = vr.public_send(k)
idx.add(v, value, pr) if value
end
end
# index attributes
pr.attributes.each do |k, v|
idx.add(k, v, pr)
end
end
idx
end
end
# idx = Index.new
# idx.add 'Color', 'rojo', p1
# idx.add 'Color', 'verde', p2
# idx.add 'Talla', 'XL', p2
#
# idx.search('Color', 'rojo') # [p1]
# idx.keys # ['Color', 'Talla']
# idx.keys_at('Color') # ['rojo', 'verde']
#
class Index
def initialize
@data = Hash.new{|h, k|
h[k] = Hash.new{|h2,k2| h2[k2] = Set.new}
}
end
def add(level1, level2, product)
@data[level1][level2].add product
end
def search(level1, level2)
@data[level1][level2].to_a
end
def keys
@data.keys
end
def keys_at(key)
@data[key].keys
end
end
class Product
attr_reader :variants, :name, :attributes, :variant_headings
def initialize(name, attributes: {}, variant_headings: {})
@variants = []
@name = name
@attributes = attributes
@variant_headings = variant_headings
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment