Avoid building identical pages with Middleman proxy - ruby

I am creating a static website with Middleman, referencing products parsed from a spreadsheet.
My table has these columns:
_________________________________
| Product Name | Price | Category |
| Pet Food | $12 | Pets |
| iPhone | $500 | Phone |
| Pet toy | $25 | Pets |
|______________|_______|__________|
I created pages that show all products in the Pets and Phone categories using a template called product_category.html. It creates a page for each unique category, eg. pets.html and phone.html.
The problem is that given the way I proceed, Middleman builds one category page for each product in the category, and then skips it as it is identical:
remote: create build/pets.html
remote: identical build/pets.html
remote: create build/iphone.html
Here is my sample for config.rb:
rows_by_categories = app.data.spreadsheet.sheet1.group_by { |row| row.category }
#Category Landings
app.data.spreadsheet.sheet1.each do |f|
proxy "/#{f.category.to_s.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')}.html", "/product_category.html", locals: {
f: {
categorytitle: f.category,
name: f.name,
all_in_category: rows_by_categories[f.category],
price: f.selling_price,
},
categories: rows_by_categories.keys,
}, ignore: true
end
I understand the loop iterates on each line of my spreadsheet and recreates a page for the corresponding category. Yet the few tries I gave, eg. modifying app.data.spreadsheet.sheet1.each do |f| into rows_by_categories.each do |f| are unsuccessful. Any clue?

As mentioned I have no experience with middleman but I am going to try and help anyway.
It appears that your main issue is that you are looping the individual items rather than the groups. Please try the below instead.
rows_by_categories = app.data.spreadsheet.sheet1.group_by { |row| row.category }
#Category Landings
rows_by_categories.each do |category, rows|
path_name = "/#{category.to_s.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')}.html"
row_data = rows.map do |row|
{
categorytitle: row.category,
name: row.name,
price: row.selling_price,
}
end
proxy path_name, "/product_category.html", locals: {
products: row_data,
categories: rows_by_categories.keys
}, ignore: true
end
Here we loop through the categories and products will now be an Array of all the products in that category rather than a single product. This will, in my limited knowledge, create a single page for each category and give you a collection products that you can loop through

Related

How to improve Ruby structure for Shopify Script Performance

I'm using a Ruby in Shopify Scripts Editor to manage as a security measure Gift With Purchase (GWP) promotions.
The script current is:
Checking if the Customer is logged in as a Professional or Unlogged
Checking if there is a minimum amount spent in the cart
Ensuring that only one "Gift" product is been added to the cart
Removing a "Gift" product if the checkout doesn't have a "Discount Code" or the minimum set in the GWP_SETTINGS = [] obj.
The problem is that it's generating too much Production Errors like "Your script exceeded the time limit." and "Your script exceeded the cpu limit."
The current usage is CPU: 5% | Memory: 8% and it's increasing dizzyingly every time we add a new GWP promotion array.
Is there a better way to structure this logic so it takes less memory to process the entire order + GWP validation?
Here is the "Line Items" structure:
cart = Input.cart
PRO_TAG = 'professional-tag'
has_pro_tag = cart.customer && cart.customer.tags.include?(PRO_TAG)
GWP_SETTINGS = [
gwp_1 = {
"variant_id" => 98989898989898,
"discount_code" => "DISCOUNT_CODE_1",
"minimum_requirement" => Money.new(cents: 50 * 100),
"user_type" => "consumer"
},
gwp_2 = {
"variant_id" => 97979797979797,
"discount_code" => "DISCOUNT_CODE_1",
"minimum_requirement" => Money.new(cents: 50 * 100),
"user_type" => "consumer"
},
gwp_3 = {
"variant_id" => 96969696969696,
"discount_code" => "DISCOUNT_CODE_1",
"minimum_requirement" => Money.new(cents: 50 * 100),
"user_type" => "consumer"
}
]
def remove_GWP(cart, variant_id)
cart.line_items.each do |item|
next if item.variant.id != variant_id
index = cart.line_items.find_index(item)
cart.line_items.delete_at(index)
end
end
def ensure_only_one_GWP_is_added(cart, variant_id)
cart.line_items.each do |item|
next if item.variant.id != variant_id
item.instance_variable_set(:#quantity, 1)
end
end
GWP_SETTINGS.each do |gwp_item_settings|
customer_has_discount = cart.discount_code && cart.discount_code.code == gwp_item_settings["discount_code"]
customer_has_minimum = cart.subtotal_price >= gwp_item_settings["minimum_requirement"]
gwp_is_for_professional = gwp_item_settings["user_type"] == "professional-tag"
#UNLOGGED
if customer_has_discount && customer_has_minimum
ensure_only_one_GWP_is_added(cart, gwp_item_settings["variant_id"])
else
remove_GWP(cart, gwp_item_settings["variant_id"])
end
#PRO
if gwp_is_for_professional && has_pro_tag
if customer_has_discount && customer_has_minimum
ensure_only_one_GWP_is_added(cart, gwp_item_settings["variant_id"])
else
remove_GWP(cart, gwp_item_settings["variant_id"])
end
end
end
Output.cart = cart
You only have 3 settings. But a customer (an order) could have 100+ line items. You know there is only ever 1 customer, 1 order and for you, 3 GWT settings to use.
Your business logic would be smarter if you looped through the line items only once. Then you have a "this is as fast as I can go, go to town" in terms of your algorithm. You cannot go faster than that.
With things like, "does this customer have an X or Y?", you do those once, not 3 times per line item!
As you check each line item, you can do your special logic for things that might AFFECT that line item.
Basically, this is basic algorithmics. You are doing the most work possible repetitively for no reason, and Shopify is puking because of it.

Shopify script — discount on Bulk Purchase for specific tags

I am trying to create a script for 25% off when 15 items tagged ‘outerwear’ are added to the cart.
The script loops through the cart for the quantity stored in items_quantities_total but this does not check that those 15 items in the cart all have the qualifying tag.
Input.cart.line_items.each_with_index do |line_item, index|
MIN_QTY_IN_CART_15 = 15
items_quantities = Input.cart&.line_items&.map { |item| item.quantity }
items_quantities_total = items_quantities.reduce(0, :+)
discountCollectionPriceBasis = line_item.line_price_was
if line_item.variant.compare_at_price
discountCollectionPriceBasis = line_item.variant.compare_at_price * line_item.quantity
end
discountCollectionPrice = discountCollectionPriceBasis * 0.75
if discountCollectionPrice < line_item.line_price
unless line_item.line_price_was < discountCollectionPrice
if line_item.variant.product.tags.include?('outerwear')
if items_quantities_total >= MIN_QTY_IN_CART_15
line_item.change_line_price(discountCollectionPrice, message: "Buy 15 outerwear items and get 25% Off!")
end
end
end
end
end
An example of the result is it currently works even if have 14 of a product not included in my 'outerwear' discount and only 1 of the intended discount item. It then applies 25% off that one item.
The problem is that you calculate items_quantities_total on all line_items, not only on the "outerwear" ones.
To fix it, you could adjust the line
items_quantities = Input.cart&.line_items&.map { |item| item.quantity }
to filter for "outerwear" tag.

Insert into select with a hard coded value with Ruby Sequel

I am using Ruby Sequel to move data into reporting tables daily. I am consolidating data from three tables into a single table. I am doing this in Redshift so I have to use disable_insert_returning. The names of the columns in the two tables match each other but not the end table which means I am using graph and set_graph_aliases.
reports = db[:reports]
report_columns = [:user_id, :purchase_date, :sku]
spoons_select_graph = {
user_id: :users,
purchase_date: :spoon_receipts,
product_id: :spoon_receipts
}
spoons = db[:spoon_receipts]
spoons_select = spoons.graph(:users, user_id: :user_id).set_graph_aliases(spoons_select_graph)
forks_select_graph = {
user_id: :users,
purchase_date: :fork_receipts,
product_id: :fork_receipts
}
forks = db[:fork_receipts]
forks_select = forks.graph(:users, user_id: :user_id).set_graph_aliases(forks_select_graph)
reports.disable_insert_returning.insert(report_columns, spoons_select)
reports.where(channel: nil).update(channel: 'spoons')
reports.disable_insert_returning.insert(report_columns, forks_select)
reports.where(channel: nil).update(channel: 'forks')
The updates are taking forever. What I would like to do is add the channel to the insert select so that I don't have to go back and update.
You didn't provide an executable setup, so I haven't tested this, but I think it will work. The basic idea is to just add a constant column of results to your select.
reports = db[:reports]
report_columns = [:user_id, :purchase_date, :sku, :channel]
spoons_select_graph = {
user_id: :users,
purchase_date: :spoon_receipts,
product_id: :spoon_receipts,
channel: [:spoon_receipts, :channel, 'spoons']
}
spoons = db[:spoon_receipts]
spoons_select = spoons.graph(:users, user_id: :user_id).set_graph_aliases(spoons_select_graph)
reports.disable_insert_returning.insert(report_columns, spoons_select)
See the documentation for set_graph_aliases for more information.

cucumber webdriverio datatable

FeatureFile
Feature: Shopper can add an item to their Grocery List
#kk
Scenario: Mutate multiple User Skills at the same time
Then If I click the row "Summary" then I should the following nested information`
| Tax | 11.50 |
| Gratuity | 4.50 |
| Total | 26.59 |
StepDefininition File
Then(/^If I click the row "([^"]*)" then I should the following nested information$/, function (rowName, data){
cconsole.log("rowName-----"+rowName)
console.log("data-----"+data)
data = dataTable.raw();
console.log(data);`
});
Error Log
If I click the row "Summary" then I should the following nested information
dataTable is not defined
ReferenceError: dataTable is not defined
at World.<anonymous> (/Users/src/__acceptance__/stepDefinition/android/wdio.apps.stepdefs.js:12:3)
at new Promise (<anonymous>)
at new F (/Users/wdio-cucumber-framework/node_modules/core-js/library/modules/_export.js:36:28)
Please help me to resolve this issue.....
I am not overly familiar with Webdriver-io, but it would appear that you are using a variable that is not defined. You are passing data into your function, but trying to use dataTable which is throwing the error.
Have you tried something like:
function(rowName, table) {
var myData = table.rowsHash();
console.log(myData);
console.log(table.raw());
}
Unrelated to your question, I would suggest separating your action steps from your validation steps
If I click the row "Summary"
Then I should see the following nested information
| Tax | 11.50 |
| Gratuity | 4.50 |
| Total | 26.59 |
You have a problem in your code on the stepDefinition file:
on the line data = dataTable.raw();... there is no variable dataTable defined.
Please try the below code:
Then(/^If I click the row "([^"]*)" then I should the following nested information$/, function (rowName, dataTable){
console.log(dataTable);
var data = dataTable.raw();
console.log(data);
data.forEach(function(element) {
console.log("Element:" + element[0]);
console.log("Element:" + element[1]);
}, this);
});

Ruby Dashing: Issues with Jobs (on a CentOS Server)

I'm having a weird event occur, where my dashing dashboard's list widget is showing erroneous data. Here's the screenshot from my live Dashing widget
Erroneous Widget
Expected Output
What follows is the code for the widget:
Code in .erb
<li data-row="1" data-col="1" data-sizex="2" data-sizey="6">
<div data-id="facebook_insights" data-view="List" data-unordered="true" data-title="Facebook Insights: Weekly Post Views" data-moreinfo="Updated every 10 seconds"</div>
</li>
Code in job .rb
require 'mysql2'
social_count = Hash.new({ value: 0 })
time = Time.new()
date_time1 = Time.new(time.year, time.month, time.day-1)
...
SCHEDULER.every '10s' do
begin
db = Mysql.new(<HOST>,<USER>,<PASS>,<DBNAME>)
mysql1 = "SELECT <VAR> FROM <TABLE> WHERE <VAR> = '#{date_time1}' ORDER BY <VAR> DESC LIMIT 1"
...
result1 = db.query(mysql1)
...
rescue
ensure
db.close
end
result1.each do |row|
strrow1 = row[0]
$value1 = strrow1.to_i
end
...
social_count[0] = {label: "1:", value: $value1}
...
send_event('facebook_insights', { items: social_count.values })
end
What is really baffling, is that this code works for a similar widget using different data in the SQL query. Can anyone help me understand why?
I checked and re-checked the data and in my other, working code, I had my $value variables defined as $valueX with X being the number. I thought to myself "Maybe the variable names are getting confused due to them having the same name", so I changed my code to
Working Code
result1.each do |row|
strrow1 = row[0]
$variable1 = strrow1.to_i
end
...
social_count[0] = {label: "1:", value: $variable1}
Et Voila! Eureka! It worked. Not sure why it still got confused with the names, but from now on, my names will be unique!

Resources