Ruby Shopify Script Tutorial

This example is part of my free email course, “Mastering Shopify Apps”. If you didn’t arrive here from an email from me, consider signing up to the course to receive more content designed to help you build Shopify applications.

In this post, we’re going to write a short Ruby script to automate a process using the Shopify API. (Would you prefer an example in a different language? Please email me!)

Our task is to ensure that the most popular products on our Shopify store are tagged with a “Best Seller” tag. (This is taken from a real-world use case for one of my clients). We’re defining “most popular” here to mean being in the top ten most-sold products over the last 30 days.

High-level design

To start to break down our requirements into something we can start turning in to code, here’s the high-level overview of the steps we’ll take:

  1. Fetch a list of orders for the last 30 days;
  2. From that list of orders, compile a list of total sales for each product;
  3. For every product in the store, ensure it has a “Best Seller” tag if it was in the 10 most-sold products. If it wasn’t, ensure that any “Best Seller” tag is removed.

Converting this into pseudo-code:

orders = shopify.get_orders_from_last_30_days

product_sales = {}

for order in orders:
  add_order_sales_to_product_sales

product_sales.sort_by_sales  

products = shopify.get_all_products

for product in products:
  if product in product_sales.first_ten
    product.add_best_seller_tag
  else
    product.remove_best_seller_tag

Ruby and Shopify

Writing our scripts in Ruby means we can use the official Ruby client for the Shopify API. Because this client is used internally at Shopify, it’s well-maintained and kept up to date with any changes in the API.

Making the client available in our script is as simple as running gem install shopify_api from the command line.

Ruby Script Implementation

Here’s the implementation I came up with. It’s heavily commented, so you should be able to read through it to understand what each part of the code is doing.

#!/bin/ruby
require 'shopify_api'

# This is an example script for the course "Mastering Shopify Apps"
# available at http://gavinballard.com/msa/. You're free to use and
# modify this script as desired.

# Define authentication parameters. You should update these with the
# authentication details for your own shop and private application.
SHOPIFY_SHOP='mastering-apps.myshopify.com'
SHOPIFY_API_KEY='797ed43b5c0c93141a5e6a2c365c37f7'
SHOPIFY_PASSWORD='4b1b805b05f3788dd604003c0db2471c'

# Configure the Shopify API with our authentication credentials.
ShopifyAPI::Base.site = "https://#{SHOPIFY_API_KEY}:#{SHOPIFY_PASSWORD}@#{SHOPIFY_SHOP}/admin"

# Fetch orders from the last 30 days. Note that we're fetching a maximum of 250
# orders, which is the most Shopify allows us to retrieve in one API call. In a
# more developed solution, you'd need to handle the situation where more than
# 250 orders had been placed in the last 30 days by fetching multiple pages of
# orders.
orders = ShopifyAPI::Order.find(:all, params: { created_at_min: (Time.now - 30.days), limit: 250 })

# Generate a hash mapping product IDs to the total quantity sold. This is done
# by iterating over every line item in every order, extracting the product ID
# and quantity from the line item as a Hash, then merging all of those hashes
# together.
product_sales = orders.map { |o| o.line_items }.flatten.inject({}) do |product_sales, line_item|
  product_sales.merge(
    Hash[line_item.product_id, line_item.quantity]
  ) { |_, current, additional| current + additional }
end

# Sort the list of product sales by quantity in descending order.
product_sales = Hash[product_sales.sort_by{ |k, v| v }.reverse]

# Take the first 10 product IDs as our list of most popular products.
most_popular_products = product_sales.keys.take(10)

# Fetch all the products in the store. As with orders, we're  limited to 250
# products here with a single API call. A production version of this script
# would also support paginating through all products.
products = ShopifyAPI::Product.find(:all, params: { limit: 250 })

# Now iterate through every product and add or remove the "Best Seller" tag as
# needed.
products.each do |product|
  # Convert tags, which are stored as a comma-separated string, into an array.
  tags = product.tags.split(',').map(&:strip)

  # Add or remove the "Best Seller" tag from the list of product tags depending
  # on whether the product is in the most popular list.
  if most_popular_products.include?(product.id)
    tags << "Best Seller"
  else
    tags.delete("Best Seller")
  end

  # Check to see if a change has been made to the tags, and if so, make the API
  # call to update the product's tags.
  updated_tags = tags.uniq.join(',')
  unless updated_tags == product.tags
    product.tags = updated_tags
    product.save
  end
end

This example is also available as a Gist. Once you’ve saved this to a file, you should now be able to trigger a re-tagging of the products in your store by running:

ruby best_sellers.rb