Skip to content

Commit

Permalink
Merge pull request #115 from Shopify/revert-114-revert-113-add-minite…
Browse files Browse the repository at this point in the history
…st-hooks

Add minitest hooks and make seeding fast. Take 2
  • Loading branch information
elsom25 committed Mar 21, 2024
2 parents 3cff817 + 1b250d0 commit 9535eea
Show file tree
Hide file tree
Showing 15 changed files with 236 additions and 52 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ gem "rubocop-shopify", require: false

group :test do
gem "minitest", "~> 5.21"
gem "minitest-hooks", "~> 1.5"
end
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ GEM
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
minitest (5.21.2)
minitest-hooks (1.5.1)
minitest (> 5.3)
mutex_m (0.2.0)
parallel (1.24.0)
parser (3.3.0.5)
Expand Down Expand Up @@ -128,6 +130,7 @@ DEPENDENCIES
byebug
jekyll (~> 4.3)
minitest (~> 5.21)
minitest-hooks (~> 1.5)
rake (~> 13.1)
rubocop-shopify
sqlite3 (~> 1.7)
Expand Down
6 changes: 6 additions & 0 deletions app/models/categories_property.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

class CategoriesProperty < ApplicationRecord
belongs_to :category
belongs_to :property, foreign_key: :property_friendly_id, primary_key: :friendly_id
end
7 changes: 6 additions & 1 deletion app/models/category.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ class Category < ApplicationRecord

has_many :children, class_name: "Category", inverse_of: :parent
belongs_to :parent, class_name: "Category", optional: true
has_and_belongs_to_many :properties

has_many :categories_properties, dependent: :destroy
has_many :properties, through: :categories_properties, foreign_key: :property_friendly_id
def property_friendly_ids=(ids)
self.properties = Property.where(friendly_id: ids)
end

validates :id,
presence: { strict: true },
Expand Down
6 changes: 6 additions & 0 deletions app/models/properties_property_value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

class PropertiesPropertyValue < ApplicationRecord
belongs_to :property
belongs_to :property_value
end
7 changes: 5 additions & 2 deletions app/models/property.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
class Property < ApplicationRecord
default_scope { order(:name) }

has_and_belongs_to_many :categories
has_and_belongs_to_many :property_values
has_many :categories_properties, dependent: :destroy, foreign_key: :property_friendly_id, primary_key: :friendly_id
has_many :categories, through: :categories_properties

has_many :properties_property_values, dependent: :destroy
has_many :property_values, through: :properties_property_values

validates :name, presence: true

Expand Down
3 changes: 2 additions & 1 deletion app/models/property_value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
class PropertyValue < ApplicationRecord
default_scope { order(Arel.sql("CASE WHEN name = 'Other' THEN 1 ELSE 0 END, name")) }

has_and_belongs_to_many :properties
has_and_belongs_to_many :properties,
join_table: :properties_property_values

validates :name, presence: true

Expand Down
35 changes: 29 additions & 6 deletions app/serializers/data/category_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module Serializers
module Data
class CategorySerializer < ObjectSerializer
class << self
delegate(:deserialize_for_insert_all, :deserialize_for_join_insert_all, to: :instance)
end

def serialize(category)
{
"id" => category.id,
Expand All @@ -13,15 +17,34 @@ def serialize(category)
end

def deserialize(hash)
id = hash["id"].downcase
parent_id = id.split("-")[0...-1].join("-").presence
name = hash["name"]

Category.new(id:, parent_id:, name:).tap do |category|
Category.new(**attributes_from(hash)).tap do |category|
category.child_ids = hash["children"] if hash["children"]
category.property_ids = Property.where(friendly_id: hash["attributes"]).pluck(:id) if hash["attributes"]
category.property_friendly_ids = hash["attributes"] if hash["attributes"]
end
end

def deserialize_for_insert_all(array)
array.map { attributes_from(_1) }
end

def deserialize_for_join_insert_all(array)
array.flat_map do |hash|
category_id = hash["id"].downcase
hash["attributes"].map do |property_friendly_id|
{ category_id:, property_friendly_id: }
end
end
end

private

def attributes_from(hash)
{
id: hash["id"].downcase,
parent_id: hash["id"].split("-")[0...-1].join("-").presence,
name: hash["name"],
}
end
end
end
end
25 changes: 25 additions & 0 deletions app/serializers/data/property_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module Serializers
module Data
class PropertySerializer < ObjectSerializer
class << self
delegate(:deserialize_for_insert_all, :deserialize_for_join_insert_all, to: :instance)
end

def serialize(property)
{
"id" => property.id,
Expand All @@ -20,6 +24,27 @@ def deserialize(hash)
property_value_ids: hash["values"].map { _1["id"] },
)
end

def deserialize_for_insert_all(array)
array.map do |hash|
{
id: hash["id"],
friendly_id: hash["friendly_id"],
name: hash["name"],
}
end
end

def deserialize_for_join_insert_all(array)
array.flat_map do |hash|
hash["values"].map do |value_hash|
{
property_id: hash["id"],
property_value_id: value_hash["id"],
}
end
end
end
end
end
end
18 changes: 16 additions & 2 deletions app/serializers/data/property_value_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module Serializers
module Data
class PropertyValueSerializer < ObjectSerializer
class << self
delegate(:deserialize_for_insert_all, to: :instance)
end

def serialize(property_value)
{
"id" => property_value.id,
Expand All @@ -11,10 +15,20 @@ def serialize(property_value)
end

def deserialize(hash)
PropertyValue.new(
PropertyValue.new(**attributes_from(hash))
end

def deserialize_for_insert_all(array)
array.map { attributes_from(_1) }
end

private

def attributes_from(hash)
{
id: hash["id"],
name: hash["name"],
)
}
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/serializers/object_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class ObjectSerializer
include Singleton

class << self
delegate :serialize, :deserialize, to: :instance
delegate(:serialize, :deserialize, to: :instance)
end

def serialize(object)
Expand Down
2 changes: 2 additions & 0 deletions application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
require_relative "app/models/category"
require_relative "app/models/property"
require_relative "app/models/property_value"
require_relative "app/models/categories_property"
require_relative "app/models/properties_property_value"

require_relative "app/serializers/object_serializer"
require_relative "app/serializers/data/category_serializer"
Expand Down
6 changes: 3 additions & 3 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@

create_table :categories_properties, id: false, force: :cascade do |t|
t.string(:category_id, null: false)
t.integer(:property_id, null: false)
t.string(:property_friendly_id, null: false)

t.index([:category_id, :property_id], unique: true)
t.index(:property_id)
t.index([:category_id, :property_friendly_id], unique: true)
t.index(:property_friendly_id)
end
create_table :properties_property_values, id: false, force: :cascade do |t|
t.integer(:property_id, null: false)
Expand Down
40 changes: 19 additions & 21 deletions db/seed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,37 @@ class Seed
class << self
def attributes_from(data)
puts "Importing values"
data.each do |property_json|
property_json["values"].each do |property_json|
property_value = Serializers::Data::PropertyValueSerializer.deserialize(property_json)
by_id = PropertyValue.find_by(id: property_value.id)

if by_id.nil?
property_value.save!
elsif by_id.name != property_value.name
puts " ⨯ Failed to import value: #{property_value.name} <#{property_value.id}> already exists as " \
"#{by_id.name} <#{by_id.id}>"
end
end
end
raw_values = data.flat_map { _1.fetch("values") }
values = Serializers::Data::PropertyValueSerializer.deserialize_for_insert_all(raw_values)
PropertyValue.insert_all(values)
puts "✓ Imported #{PropertyValue.count} values"

puts "Importing properties"
data.each do |json|
Serializers::Data::PropertySerializer.deserialize(json).save!
end
properties = Serializers::Data::PropertySerializer.deserialize_for_insert_all(data)
Property.insert_all(properties)
puts "✓ Imported #{Property.count} properties"

puts "Importing property ↔ value relationships"
joins = Serializers::Data::PropertySerializer.deserialize_for_join_insert_all(data)
PropertiesPropertyValue.insert_all(joins)
puts "✓ Imported #{PropertiesPropertyValue.count} property ↔ value relationships"
end

def categories_from(data)
puts "Importing #{data.count} category verticals"
data.each do |vertical_json|
puts " → #{vertical_json.first.fetch("name")}"
vertical_json.each do |category_json|
unless Serializers::Data::CategorySerializer.deserialize(category_json.except("children")).save
puts " ⨯ Failed to import category: #{category_json["name"]} <#{category_json["id"]}>"
end
end
categories = Serializers::Data::CategorySerializer.deserialize_for_insert_all(vertical_json)
Category.insert_all(categories)
end
puts "✓ Imported #{Category.count} categories"

puts "Importing category relationships"
data.each do |vertical_json|
joins = Serializers::Data::CategorySerializer.deserialize_for_join_insert_all(vertical_json)
CategoriesProperty.insert_all(joins)
end
puts "✓ Imported #{CategoriesProperty.count} category ↔ property relationships"
end
end
end
Expand Down

0 comments on commit 9535eea

Please sign in to comment.