Refactor, move product type matching to DfcProductTypeFactory

It keeps SuppliedProductBuilder and move all the matching logic to its
own class
This commit is contained in:
Gaetan Craig-Riou
2023-12-22 14:27:27 +11:00
parent d8b56d5c16
commit c40ccb8e86
4 changed files with 119 additions and 94 deletions

View File

@@ -0,0 +1,63 @@
# frozen_string_literal: true
require 'singleton'
class DfcProductTypeFactory
include Singleton
def self.for(dfc_id)
instance.for(dfc_id)
end
def initialize
@product_types = {}
populate_product_types
end
def for(dfc_id)
return nil if @product_types[dfc_id].nil?
call_dfc_product_type(@product_types[dfc_id])
end
private
def populate_product_types
DfcLoader.connector.PRODUCT_TYPES.topConcepts.each do |product_type|
stack = []
record_type(stack, product_type.to_s)
end
end
def record_type(stack, product_type)
name = product_type.to_s
current_stack = stack.dup.push(name)
type = call_dfc_product_type(current_stack)
id = type.semanticId
@product_types[id] = current_stack
# Narrower product types are defined as class method on the current product type object
narrowers = type.methods(false).sort
# Leaf node
return if narrowers.empty?
narrowers.each do |narrower|
# recursive call
record_type(current_stack, narrower)
end
end
# Callproduct type method ie: DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK
def call_dfc_product_type(product_type_path)
type = DfcLoader.connector.PRODUCT_TYPES
product_type_path.each do |pt|
type = type.public_send(pt)
end
type
end
end

View File

@@ -1,8 +1,6 @@
# frozen_string_literal: true
class SuppliedProductBuilder < DfcBuilder
PRODUCT_TYPES = {} # rubocop:disable Style/MutableConstant
def self.supplied_product(variant)
id = urls.enterprise_supplied_product_url(
enterprise_id: variant.product.supplier_id,
@@ -63,51 +61,7 @@ class SuppliedProductBuilder < DfcBuilder
def self.product_type(variant)
taxon_dfc_id = variant.product.primary_taxon&.dfc_id
return nil if taxon_dfc_id.nil?
populate_product_types if PRODUCT_TYPES.empty?
return nil if PRODUCT_TYPES[taxon_dfc_id].nil?
call_dfc_product_type(PRODUCT_TYPES[taxon_dfc_id])
end
def self.populate_product_types
DfcLoader.connector.PRODUCT_TYPES.topConcepts.each do |product_type|
stack = []
record_type(stack, product_type.to_s)
end
end
def self.record_type(stack, product_type)
name = product_type.to_s
current_stack = stack.dup.push(name)
type = call_dfc_product_type(current_stack)
id = type.semanticId
PRODUCT_TYPES[id] = current_stack
# Narrower product types are defined as class method on the current product type object
narrowers = type.methods(false).sort
# Leaf node
return if narrowers.empty?
narrowers.each do |narrower|
# recursive call
record_type(current_stack, narrower)
end
end
# Callproduct type method ie: DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK
def self.call_dfc_product_type(product_type_path)
type = DfcLoader.connector.PRODUCT_TYPES
product_type_path.each do |pt|
type = type.public_send(pt)
end
type
DfcProductTypeFactory.for(taxon_dfc_id)
end
def self.taxon(supplied_product)
@@ -115,6 +69,5 @@ class SuppliedProductBuilder < DfcBuilder
Spree::Taxon.find_by(dfc_id: )
end
private_class_method :product_type, :populate_product_types, :record_type, :call_dfc_product_type,
:taxon
private_class_method :product_type, :taxon
end

View File

@@ -0,0 +1,49 @@
# frozen_string_literal: true
require_relative "../spec_helper"
describe DfcProductTypeFactory do
describe ".for" do
let(:dfc_id) {
"https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#drink"
}
it "assigns a top level product type" do
drink = DfcLoader.connector.PRODUCT_TYPES.DRINK
expect(described_class.for(dfc_id)).to eq drink
end
context "with second level product type" do
let(:dfc_id) {
"https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink"
}
it "assigns a second level product type" do
soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK
expect(described_class.for(dfc_id)).to eq soft_drink
end
end
context "with leaf level product type" do
let(:dfc_id) {
"https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#lemonade"
}
it "assigns a leaf level product type" do
lemonade = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE
expect(described_class.for(dfc_id)).to eq lemonade
end
end
context "with non existing product type" do
let(:dfc_id) { "other" }
it "returns nil" do
expect(described_class.for(dfc_id)).to be_nil
end
end
end
end

View File

@@ -15,8 +15,8 @@ describe SuppliedProductBuilder do
let(:taxon) {
build(
:taxon,
name: "Drink",
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#drink"
name: "Soft Drink",
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink"
)
}
@@ -54,50 +54,10 @@ describe SuppliedProductBuilder do
context "product_type mapping" do
subject(:product) { builder.supplied_product(variant) }
it "assigns a top level product type" do
drink = DfcLoader.connector.PRODUCT_TYPES.DRINK
it "assigns a product type" do
soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK
expect(product.productType).to eq drink
end
context "with second level product type" do
let(:taxon) {
build(
:taxon,
name: "Soft Drink",
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink"
)
}
it "assigns a second level product type" do
soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK
expect(product.productType).to eq soft_drink
end
end
context "with leaf level product type" do
let(:taxon) {
build(
:taxon,
name: "Lemonade",
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#lemonade"
)
}
it "assigns a leaf level product type" do
lemonade = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE
expect(product.productType).to eq lemonade
end
end
context "with non existing product type" do
let(:taxon) { build(:taxon, name: "other", dfc_id: "other") }
it "returns nil" do
expect(product.productType).to be_nil
end
expect(product.productType).to eq soft_drink
end
context "when no taxon set" do