Last active
July 27, 2017 19:32
-
-
Save ismasan/eabc3d9c5505c1da062db4cc5fbb5a7f to your computer and use it in GitHub Desktop.
Proof of concept for variants with associated headings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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