mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-04 22:16:08 +00:00
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:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user