James MacAulay 14 years ago
parent
commit
1dbff64899
5 changed files with 281 additions and 8 deletions
  1. 4 0
      bin/shopify
  2. 156 0
      lib/shopify_api/cli.rb
  3. 11 8
      shopify_api.gemspec
  4. 109 0
      test/cli_test.rb
  5. 1 0
      test/test_helper.rb

+ 4 - 0
bin/shopify

@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+require File.expand_path('../../lib/shopify_api',__FILE__)
+require File.expand_path('../../lib/shopify_api/cli',__FILE__)
+ShopifyAPI::Cli.start(ARGV)

+ 156 - 0
lib/shopify_api/cli.rb

@@ -0,0 +1,156 @@
+require 'thor'
+
+module ShopifyAPI
+  class Cli < Thor
+    include Thor::Actions
+    
+    class ConfigFileError < StandardError
+    end
+    
+    desc "list", "list available connections"
+    def list
+      available_connections.each do |c|
+        prefix = default?(c) ? " * " : "   "
+        puts prefix + c
+      end
+    end
+    
+    desc "add CONNECTION", "create a config file for a connection named CONNECTION"
+    def add(connection)
+      file = config_file(connection)
+      if File.exist?(file)
+        raise ConfigFileError, "There is already a config file at #{file}"
+      else
+        config = {'protocol' => 'https'}
+        config['domain']   = ask("Domain? (leave blank for #{connection}.myshopify.com)")
+        config['domain']   = "#{connection}.myshopify.com" if config['domain'].blank?
+        puts "\nopen https://#{config['domain']}/admin/api in your browser to get API credentials\n"
+        config['api_key']  = ask("API key?")
+        config['password'] = ask("Password?")
+        create_file(file, config.to_yaml)
+      end
+      if available_connections.one?
+        default(connection)
+      end
+    end
+    
+    desc "remove CONNECTION", "remove the config file for CONNECTION"
+    def remove(connection)
+      file = config_file(connection)
+      if File.exist?(file)
+        remove_file(default_symlink) if default?(connection)
+        remove_file(file)
+      else
+        no_config_file_error(file)
+      end
+    end
+    
+    desc "edit [CONNECTION]", "open the config file for CONNECTION with your default editor"
+    def edit(connection=nil)
+      file = config_file(connection)
+      if File.exist?(file)
+        if ENV['EDITOR'].present?
+          `#{ENV['EDITOR']} #{file}`
+        else
+          puts "Please set an editor in the EDITOR environment variable"
+        end
+      else
+        no_config_file_error(file)
+      end
+    end
+    
+    desc "show [CONNECTION]", "output the location and contents of the CONNECTION's config file"
+    def show(connection=nil)
+      connection ||= default_connection
+      file = config_file(connection)
+      if File.exist?(file)
+        puts file
+        puts `cat #{file}`
+      else
+        no_config_file_error(file)
+      end
+    end
+    
+    desc "default [CONNECTION]", "show the default connection, or make CONNECTION the default"
+    def default(connection=nil)
+      if connection
+        target = config_file(connection)
+        if File.exist?(target)
+          remove_file(default_symlink)
+          `ln -s #{target} #{default_symlink}`
+        else
+          no_config_file_error(file)
+        end
+      end
+      if File.exist?(default_symlink)
+        puts "Default connection is #{default_connection}"
+      else
+        puts "There is no default connection set"
+      end
+    end
+    
+    desc "console [CONNECTION]", "start an API console for CONNECTION"
+    def console(connection=nil)
+      file = config_file(connection)
+      
+      config = YAML.load(File.read(file))
+      puts "using #{config['domain']}"
+      ShopifyAPI::Base.site = site_from_config(config)
+      
+      require 'irb'
+      require 'irb/completion'
+      ARGV.clear
+      IRB.start
+    end
+    
+    private
+    
+    def shop_config_dir
+      @shop_config_dir ||= File.join(ENV['HOME'], '.shopify', 'shops')
+    end
+    
+    def default_symlink
+      @default_symlink ||= File.join(shop_config_dir, 'default')
+    end
+    
+    def config_file(connection)
+      if connection
+        File.join(shop_config_dir, "#{connection}.yml")
+      else
+        default_symlink
+      end
+    end
+    
+    def site_from_config(config)
+      protocol = config['protocol'] || 'https'
+      api_key  = config['api_key']
+      password = config['password']
+      domain   = config['domain']
+    
+      ShopifyAPI::Base.site = "#{protocol}://#{api_key}:#{password}@#{domain}/admin"
+    end
+    
+    def available_connections
+      @available_connections ||= begin
+        pattern = File.join(shop_config_dir, "*.yml")
+        Dir.glob(pattern).map { |f| File.basename(f, ".yml") }
+      end
+    end
+    
+    def default_connection_target
+      @default_connection_target ||= File.readlink(default_symlink)
+    end
+    
+    def default_connection
+      @default_connection ||= File.basename(default_connection_target, ".yml")
+    end
+    
+    def default?(connection)
+      default_connection == connection
+    end
+    
+    def no_config_file_error(filename)
+      raise ConfigFileError, "There is no config file at #{file}"
+    end
+  end
+end

+ 11 - 8
shopify_api.gemspec

@@ -76,6 +76,7 @@ Copyright (c) 2009 "JadedPixel inc.". See LICENSE for details.
      "README.rdoc",
      "Rakefile",
      "lib/shopify_api.rb",
+     "lib/shopify_api/cli.rb",
      "shopify_api.gemspec",
      "test/order_test.rb",
      "test/shopify_api_test.rb",
@@ -92,18 +93,20 @@ Copyright (c) 2009 "JadedPixel inc.". See LICENSE for details.
      "test/shopify_api_test.rb",
      "test/test_helper.rb"
   ]
+  s.executables = ['shopify']
 
-  if s.respond_to? :specification_version then
+  if s.respond_to? :specification_version
     current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
     s.specification_version = 3
-
-    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
-      s.add_runtime_dependency(%q<activeresource>, [">= 2.2.2"])
-    else
-      s.add_dependency(%q<activeresource>, [">= 2.2.2"])
-    end
+  end
+  
+  s.add_dependency("activeresource", [">= 2.2.2"])
+  s.add_dependency("thor", [">= 0.14.4"])
+  
+  if s.respond_to?(:add_development_dependency)
+    s.add_development_dependency("mocha", ">= 0.9.8")
   else
-    s.add_dependency(%q<activeresource>, [">= 2.2.2"])
+    s.add_dependency("mocha", ">= 0.9.8")
   end
 end
 

+ 109 - 0
test/cli_test.rb

@@ -0,0 +1,109 @@
+require 'test_helper'
+require 'shopify_api/cli'
+require 'fileutils'
+
+class CliTest < Test::Unit::TestCase
+  def setup
+    @test_home = File.join(File.expand_path(File.dirname(__FILE__)), 'files', 'home')
+    @shop_config_dir = File.join(@test_home, '.shopify', 'shops')
+    @default_symlink = File.join(@shop_config_dir, 'default')
+    `rm -rf #{@test_home}`
+    ENV['HOME'] = @test_home
+    @cli = ShopifyAPI::Cli.new
+    
+    FileUtils.mkdir_p(@shop_config_dir)
+    File.open(config_file('foo'), 'w') do |file|
+      file.puts valid_options.merge('domain' => 'foo.myshopify.com').to_yaml
+    end
+    File.symlink(config_file('foo'), @default_symlink)
+    File.open(config_file('bar'), 'w') do |file|
+      file.puts valid_options.merge('domain' => 'bar.myshopify.com').to_yaml
+    end
+  end
+  
+  def teardown
+    `rm -rf #{@test_home}`
+  end
+  
+  test "add with blank domain" do
+    `rm -rf #{@shop_config_dir}/*`
+    $stdout.expects(:print).with("Domain? (leave blank for foo.myshopify.com) ")
+    $stdout.expects(:print).with("API key? ")
+    $stdout.expects(:print).with("Password? ")
+    $stdin.expects(:gets).times(3).returns("", "key", "pass")
+    @cli.expects(:puts).with("\nopen https://foo.myshopify.com/admin/api in your browser to get API credentials\n")
+    @cli.expects(:puts).with("Default connection is foo")
+    
+    @cli.add('foo')
+    
+    config = YAML.load(File.read(config_file('foo')))
+    assert_equal 'foo.myshopify.com', config['domain']
+    assert_equal 'key', config['api_key']
+    assert_equal 'pass', config['password']
+    assert_equal 'https', config['protocol']
+    assert_equal config_file('foo'), File.readlink(@default_symlink)
+  end
+  
+  test "add with explicit domain" do
+    `rm -rf #{@shop_config_dir}/*`
+    $stdout.expects(:print).with("Domain? (leave blank for foo.myshopify.com) ")
+    $stdout.expects(:print).with("API key? ")
+    $stdout.expects(:print).with("Password? ")
+    $stdin.expects(:gets).times(3).returns("bar.myshopify.com", "key", "pass")
+    @cli.expects(:puts).with("\nopen https://bar.myshopify.com/admin/api in your browser to get API credentials\n")
+    @cli.expects(:puts).with("Default connection is foo")
+    
+    @cli.add('foo')
+    
+    config = YAML.load(File.read(config_file('foo')))
+    assert_equal 'bar.myshopify.com', config['domain']
+  end
+  
+  test "list" do
+    @cli.expects(:puts).with("   bar")
+    @cli.expects(:puts).with(" * foo")
+    
+    @cli.list
+  end
+  
+  test "show default" do
+    @cli.expects(:puts).with("Default connection is foo")
+    
+    @cli.default
+  end
+  
+  test "set default" do
+    @cli.expects(:puts).with("Default connection is bar")
+    
+    @cli.default('bar')
+    
+    assert_equal config_file('bar'), File.readlink(@default_symlink)
+  end
+  
+  test "remove default connection" do
+    @cli.remove('foo')
+    
+    assert !File.exist?(@default_symlink)
+    assert !File.exist?(config_file('foo'))
+    assert File.exist?(config_file('bar'))
+  end
+  
+  test "remove non-default connection" do
+    @cli.remove('bar')
+    
+    assert_equal config_file('foo'), File.readlink(@default_symlink)
+    assert File.exist?(config_file('foo'))
+    assert !File.exist?(config_file('bar'))
+  end
+  
+  private
+  
+  def valid_options
+    {'domain' => 'snowdevil.myshopify.com', 'api_key' => 'key', 'password' => 'pass', 'protocol' => 'https'}
+  end
+  
+  def config_file(connection)
+    File.join(@shop_config_dir, "#{connection}.yml")
+  end
+  
+end

+ 1 - 0
test/test_helper.rb

@@ -1,5 +1,6 @@
 require 'rubygems'
 require 'test/unit'
+require 'mocha'
 
 $LOAD_PATH.unshift(File.dirname(__FILE__))
 $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))