Permalink
Browse files

Initial commit. Can upload and download all theme files, also have si…

…mple 'watch' functionality
  • Loading branch information...
0 parents commit 4eedeb7367e60afaa620f82f67b2f878e3c81cea @jduff jduff committed Jun 20, 2011
Showing with 168 additions and 0 deletions.
  1. +4 −0 .gitignore
  2. +4 −0 Gemfile
  3. +2 −0 Rakefile
  4. +4 −0 bin/theme
  5. +3 −0 lib/shopify_theme.rb
  6. +124 −0 lib/shopify_theme/cli.rb
  7. +3 −0 lib/shopify_theme/version.rb
  8. +24 −0 shopify_theme.gemspec
@@ -0,0 +1,4 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in shopify_theme.gemspec
+gemspec
@@ -0,0 +1,2 @@
+require 'bundler'
+Bundler::GemHelper.install_tasks
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+require File.expand_path('../../lib/shopify_theme',__FILE__)
+require File.expand_path('../../lib/shopify_theme/cli',__FILE__)
+ShopifyTheme::Cli.start(ARGV)
@@ -0,0 +1,3 @@
+module ShopifyTheme
+ # Your code goes here...
+end
@@ -0,0 +1,124 @@
+require 'rubygems'
+require 'thor'
+require 'yaml'
+require 'abbrev'
+require 'httparty'
+require 'base64'
+require 'ftools'
+require 'json'
+
+module ShopifyTheme
+ class Cli < Thor
+ include Thor::Actions
+
+ BINARY_EXTENSIONS = %w(png gif jpg jpeg eot svg ttf woff swf)
+ IGNORE = %w(config.yml)
+
+ tasks.keys.abbrev.each do |shortcut, command|
+ map shortcut => command.to_sym
+ end
+
+ desc "configure API_KEY PASSWORD STORE", "generate a config file for the store to connect to"
+ def generate(api_key=nil, password=nil, store=nil)
+ config = {:api_key => api_key, :password => password, :store => store}
+ create_file('config.yml', config.to_yaml)
+ end
+
+ desc "download", "download the shops current theme"
+ def download
+ setup
+ asset_list.each do |a|
+ asset = get_asset(a)
+
+ if asset['value']
+ content = asset['value'].gsub("\r", "")
+ elsif asset['attachment']
+ content = Base64.decode64(asset['attachment'])
+ end
+
+ File.makedirs(File.dirname(a))
+ File.open(a, "w") {|f| f.write content} if content
+ print "."
+ $stdout.flush
+ end
+ puts "\nDone."
+ end
+
+ desc "upload", "upload all theme files to shop"
+ def upload
+ setup
+
+ local_assets_list.each do |a|
+ send_asset(a)
+ end
+ puts "Done."
+ end
+
+ desc "watch", "upload individual files as they change"
+ method_option :quiet, :type => :boolean, :default => false
+ def watch
+ setup
+
+ loop do
+ modified_files.each do |a|
+ send_asset(a, options['quiet'])
+ end
+ sleep(1)
+ end
+ rescue Interrupt
+ puts ""
+ end
+
+ private
+ def setup
+ @config = YAML.load(File.read('config.yml'))
+ @default_options = {:basic_auth => {:username => @config[:api_key], :password => @config[:password]}}
+ @base_uri = "http://#{@config[:store]}"
+ end
+
+ def asset_list
+ response = HTTParty.get("#{@base_uri}/admin/assets.json", @default_options)
+ assets = JSON.parse(response.body)["assets"].collect {|a| a['key'] }
+ # Remove any .css files if a .css.liquid file exists
+ assets.reject{|a| assets.include?("#{a}.liquid") }
+ end
+
+ def get_asset(asset)
+ response = HTTParty.get("#{@base_uri}/admin/assets.json", @default_options.merge(:query =>{:asset => {:key => asset}}))
+ # HTTParty json parsing is broken?
+ JSON.parse(response.body)["asset"]
+ end
+
+ def local_assets_list
+ Dir.glob(File.join("**", "*")).reject{ |p| File.directory?(p) || IGNORE.include?(p)}
+ end
+
+ def send_asset(asset, quiet=false)
+ data = {:key => asset}
+ if (content = File.read(asset)).is_binary_data? || BINARY_EXTENSIONS.include?(File.extname(asset).gsub('.',''))
+ data.merge!(:attachment => Base64.encode64(content))
+ else
+ data.merge!(:value => content)
+ end
+
+ response = HTTParty.put("#{@base_uri}/admin/assets.json", @default_options.merge(:body =>{:asset => data}))
+
+ if response.success?
+ puts "Uploaded: #{asset}" unless quiet
+ else
+ puts "Error: Could not upload #{asset} to #{@config['store']}"
+ end
+ end
+
+ def modified_files
+ @reference_time ||= Time.now
+ checked = Time.now
+
+ files = local_assets_list.select do |asset|
+ File.mtime(asset) > @reference_time
+ end
+ @reference_time = checked unless files.empty?
+ files
+ end
+ end
+end
@@ -0,0 +1,3 @@
+module ShopifyTheme
+ VERSION = "0.0.1"
+end
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "shopify_theme/version"
+
+Gem::Specification.new do |s|
+ s.name = "shopify_theme"
+ s.version = ShopifyTheme::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["John Duff"]
+ s.email = ["john.duff@shopify.com"]
+ s.homepage = ""
+ s.summary = %q{Command line tool for developing themes}
+ s.description = %q{Command line tool to help with developing Shopify themes. Provides simple commands to download and upload a theme, as well as watch a directory and upload files as they change.}
+
+ s.rubyforge_project = "shopify_theme"
+ s.add_dependency("thor", [">= 0.14.4"])
+ s.add_dependency("httparty")
+ s.add_dependency("json")
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+end

0 comments on commit 4eedeb7

Please sign in to comment.