Skip to content

Instantly share code, notes, and snippets.

@ismasan
Last active December 15, 2016 13:40
Show Gist options
  • Save ismasan/b9d0d7f82fcd0d723ef553298bcfbf98 to your computer and use it in GitHub Desktop.
Save ismasan/b9d0d7f82fcd0d723ef553298bcfbf98 to your computer and use it in GitHub Desktop.
Backup a Bootic theme using the Bootic CLI
require "fileutils"
require "open-uri"
require "thread"
require "listen"
# dependencies:
# gem install listen
#
# Put this file in ~/btc/themes.rb
# Theme management
# Downloads a shop's theme templates and assets
# Usage
#
# # if shop is acme.bootic.net
#
# btc themes download acme -d ~/backups
#
# cd acme
#
# btc themes watch
#
class Themes < BooticCli::Command
desc "download acme", "download a shop's theme"
option :d, banner: "<target_directory>", default: Dir.pwd
def download(subdomain = nil)
shop = if subdomain
root.all_shops(subdomains: subdomain).first
else
root.shops.first
end
say("no shop with subdomain #{subdomain}") and return unless shop
dir = File.join(options["d"], shop.subdomain)
FileUtils.rm_rf(dir) if File.directory?(dir)
pull dir
end
desc "pull", "pull latest theme changes into current directory"
def pull(dir = ".")
# re-fetch theme
theme = shop.theme
assets_dir = File.join(dir, "assets")
FileUtils.mkdir_p assets_dir
download_templates(dir, theme.templates)
download_assets(assets_dir, theme.assets)
end
desc "push", "push all theme files to current shop"
def push(dir = ".")
theme = shop.theme
Dir.glob(File.join(dir, "**/*")).each do |path|
upsert theme, File.expand_path(path) unless File.directory?(path)
end
end
desc "watch acme", "watch and update"
def watch(dir = ".")
theme = shop.theme
listener = Listen.to(dir) do |modified, added, removed|
if modified.any?
modified.each do |path|
upsert theme, path
end
end
if added.any?
added.each do |path|
upsert theme, path
end
end
if removed.any?
removed.each do |path|
delete theme, path
end
end
# update local cache
theme = theme.self
end
puts "watching #{dir}"
listener.start
# ctrl-c
Signal.trap("INT") {
listener.stop
puts "bye bye"
exit
}
sleep
end
private
def upsert(theme, path)
if path =~ /assets/
upsert_asset theme, path
else
upsert_template theme, path
end
end
def delete(theme, path)
if path =~ /assets/
delete_asset theme, path
else
delete_template theme, path
end
end
def delete_template(theme, path)
fname = File.basename(path)
tpl = theme.templates.find{|t| t.file_name == fname}
return unless tpl
if tpl.has?(:delete_template)
puts "deleting #{path}"
tpl.delete_template
else
puts "template cannot be deleted. Re-fetching"
write(path, tpl.body, "w")
end
end
def delete_asset(theme, path)
fname = File.basename(path)
asset = theme.assets.find{|t| t.file_name == fname}
return unless asset
puts "deleting #{path}"
asset.delete_theme_asset
end
def upsert_template(theme, path)
confirm_upload(theme.create_template(
file_name: File.basename(path),
body: File.read(path)
), path)
end
def upsert_asset(theme, path)
confirm_upload(theme.create_theme_asset(
file_name: File.basename(path),
data: File.new(path)
), path)
end
def confirm_upload(entity, path)
if entity.has?(:errors)
entity.errors.each do |e|
puts [e.field, e.messages.join(", ")].join(": ")
end
else
puts "modified absolute path: #{path}"
end
end
def download_templates(dir, templates)
templates.each do |t|
puts write(File.join(dir, t.file_name), t.body, "w")
end
end
def download_assets(dir, assets)
queue = Queue.new
threads = assets.map do |a|
Thread.new do
file = open(a.rels[:file].href)
queue << write(File.join(dir, a.file_name), file.read, "wb")
end
end
printer = Thread.new do
while path = queue.pop
puts path
end
end
threads.map &:join
queue << false
printer.join
end
def write(path, content, mode)
File.open(path, mode) do |io|
io.write content
end
path
end
declare self, "manage shop themes"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment