lib/jim/cli.rb in jim-0.2.3 vs lib/jim/cli.rb in jim-0.3.0.pre
- old
+ new
@@ -1,214 +1,252 @@
-require 'optparse'
-require 'readline'
+require 'thor'
module Jim
+ # CLI handles the command line interface for the `jim` binary. It is a `Thor`
+ # application.
+ class CLI < ::Thor
+ include Thor::Actions
- # CLI handles the command line interface for the `jim` binary.
- # The layout is farily simple. Options are parsed using optparse.rb and
- # the different public methods represent 1-1 the commands provided by the bin.
- class CLI
+ attr_accessor :jimfile, :jimhome, :debug, :force
- attr_accessor :jimfile, :jimhome, :force, :stdout
+ source_root File.dirname(__FILE__) + '/templates'
+ class_option "jimhome",
+ :type => :string,
+ :banner => "set the install path/JIMHOME dir (default ~/.jim)"
+
+ class_option "jimfile",
+ :type => :string,
+ :aliases => '-j',
+ :banner => "load specific Jimfile at path (default ./Jimfile)"
+
+ class_option "force",
+ :default => false,
+ :aliases => '-f',
+ :banner => "force file creation/overwrite"
+
+ class_option "debug",
+ :default => false,
+ :aliases => '-d',
+ :banner => "set log level to debug"
+
+ class_option "version",
+ :type => :boolean,
+ :aliases => '-v',
+ :banner => "print version and exit"
+
# create a new instance with the args passed from the command line i.e. ARGV
- def initialize(args)
- @output = ""
+ def initialize(*)
+ super
+ if options[:version]
+ say "jim #{Jim::VERSION}", :red
+ exit
+ end
# set the default jimhome
- self.jimhome = Pathname.new(ENV['JIMHOME'] || '~/.jim').expand_path
+ self.jimhome = Pathname.new(options[:jimhome] || ENV['JIMHOME'] || '~/.jim').expand_path
# parse the options
- self.jimfile = Pathname.new('Jimfile')
- @args = parse_options(args)
- ## try to run based on args
+ self.jimfile = Pathname.new(options[:jimfile] || 'Jimfile').expand_path
+ self.force = options[:force]
+ self.debug = options[:debug]
+ logger.level = Logger::DEBUG if debug
end
- # method called by the bin directly after initialization.
- def run(reraise = false)
- command = @args.shift
- if command && respond_to?(command)
- self.send(command, *@args)
- elsif command.nil? || command.strip == ''
- cheat
- else
- @output << "No action found for #{command}. Run -h for help."
- end
- @output
- rescue ArgumentError => e
- @output << "#{e.message} for #{command}"
- raise e if reraise
- rescue Jim::FileExists => e
- @output << "#{e.message} already exists, bailing. Use --force if you're sure"
- raise e if reraise
- rescue => e
- @output << e.message + " (#{e.class})"
- raise e if reraise
- end
-
- # list the possible commands to the logger
- def commands
- logger.info "Usage: jim [options] [command] [args]\n"
- logger.info "Commands:"
- logger.info template('commands')
- end
-
- # list the possible commands without detailed descriptions
- def cheat
- logger.info "Usage: jim [options] [command] [args]\n"
- logger.info "Commands:"
- logger.info [*template('commands')].grep(/^\w/).join
- logger.info "run commands for details"
- end
- alias :help :cheat
-
- # initialize the current dir with a new Jimfile
+ desc 'init [APPDIR]',
+ 'Create an example Jimfile at path or the current directory if path is omitted'
def init(dir = nil)
dir = Pathname.new(dir || '')
jimfile_path = dir + 'Jimfile'
- if jimfile_path.readable? && !force
- raise Jim::FileExists.new(jimfile_path)
- else
- File.open(jimfile_path, 'w') do |f|
- f << template('jimfile')
- end
- logger.info "wrote Jimfile to #{jimfile_path}"
- end
+ template('jimfile', jimfile_path)
end
- # install the file/project `url` into `jimhome`
+ desc 'install <URL> [NAME] [VERSION]',
+ "Install the file(s) at url into the JIMHOME directory."
+
+ long_desc <<-EOT
+ Install the file(s) at url into the JIMHOME directory. URL can be any path
+ or url that Downlow understands. This means:
+
+ jim install http://code.jquery.com/jquery-1.4.1.js\n
+ jim install ../sammy/\n
+ jim install gh://quirkey/sammy\n
+ jim install git://github.com/jquery/jquery.git\n
+ EOT
def install(url, name = false, version = false)
Jim::Installer.new(url, jimhome, :force => force, :name => name, :version => version).install
end
- # bundle the files specified in Jimfile into `to`
- def bundle(to = nil)
- to = STDOUT if stdout
- io = bundler.bundle!(to)
- logger.info "Wrote #{File.size(io.path) / 1024}kb" if io.respond_to? :path
+ desc 'bundle [BUNDLE_NAME]',
+ "Bundle the files specified in Jimfile"
+ long_desc <<-EOT
+ Concatenate all the bundles listed in a Jimfile and save them to the bundle dir
+ specified in the options or in the Jimfile.
+
+ If [BUNDLE_NAME] is specified, only bundles that specific bundle.
+
+ If no Jimfile is set in the options, assumes ./Jimfile.
+ EOT
+ method_option "bundle_dir",
+ :type => :string,
+ :banner => "override the bundle_dir set in the Jimfile"
+ method_option "stdout",
+ :default => false,
+ :aliases => '-o',
+ :type => :boolean,
+ :banner => "write the bundle to STDOUT"
+ def bundle(bundle_name = nil)
+ make_bundle(bundle_name, false)
end
- # compress the files specified in Jimfile into `to`
- def compress(to = nil)
- to = STDOUT if stdout
- io = bundler.compress!(to)
- logger.info "Wrote #{File.size(io.path) / 1024}kb" if io.respond_to? :path
+ desc "compress [BUNDLE_NAME]",
+ "Bundle all the files listed in a Jimfile, run through the google closure " +
+ "compiler and save them to [COMPRESSED_PATH]."
+ long_desc <<-EOT
+ Concatenate all the bundles listed in a Jimfile, run them through the google
+ closure compiler and save them to the bundle dirspecified in the options or
+ in the Jimfile.
+
+ If a [BUNDLE_NAME] is specified, only bundle and compress that bundle.
+
+ If no Jimfile is set in the options, assumes ./Jimfile.
+ EOT
+ method_option "bundle_dir",
+ :type => :string,
+ :banner => "override the bundle_dir set in the Jimfile"
+ method_option "stdout",
+ :default => false,
+ :aliases => '-o',
+ :type => :boolean,
+ :banner => "write the bundle to STDOUT"
+ def compress(bundle_name = nil)
+ make_bundle(bundle_name, true)
end
- # copy/vendor all the files specified in Jimfile to `dir`
+ desc "vendor [VENDOR_DIR]",
+ "Copy all the files listed in Jimfile to the vendor_dir"
def vendor(dir = nil)
- bundler.vendor!(dir, force)
+ dir = bundler.vendor!(dir, force)
+ say("Vendored files to #{dir}", :green)
+ rescue Jim::Error => e
+ say e.message, :red
end
- # list the only the _installed_ projects and versions.
- # Match names against `search` if supplied.
+ desc "list [SEARCH]",
+ "List all the installed packages and their versions, optionally limiting by [SEARCH]"
def list(search = nil)
- logger.info "Getting list of installed files in\n#{installed_index.directories.join(':')}"
- logger.info "Searching for '#{search}'" if search
+ say "Getting list of installed files in"
+ say("#{installed_index.directories.join(':')}", :yellow)
+ say("Searching for '#{search}'", :yellow) if search
list = installed_index.list(search)
- logger.info "Installed:"
+ say "Installed:"
print_version_list(list)
end
- alias :installed :list
+ map "installed" => "list"
- # list all available projects and versions including those in the local path, or
- # paths specified in a Jimfile.
- # Match names against `search` if supplied.
+ desc "available [SEARCH]",
+ "List all available projects and versions " +
+ "including those in the local path, or paths specified in a Jimfile"
def available(search = nil)
- logger.info "Getting list of all available files in\n#{index.directories.join("\n")}"
- logger.info "Searching for '#{search}'" if search
+ say "Getting list of all available files in\n#{index.directories.join("\n")}"
+ say "Searching for '#{search}'" if search
list = index.list(search)
- logger.info "Available:"
+ say "Available:"
print_version_list(list)
end
- # Iterates over matching files and prompts for removal
+ desc "remove <NAME> [VERSION]",
+ "Iterate through the install files and prompt for the removal of those " +
+ "matching the supplied NAME and VERSION"
def remove(name, version = nil)
- logger.info "Looking for files matching #{name} #{version}"
+ say "Looking for files matching #{name} #{version}"
files = installed_index.find_all(name, version)
if files.length > 0
- logger.info "Found #{files.length} matching files"
+ say "Found #{files.length} matching files"
removed = 0
files.each do |filename|
- response = Readline.readline("Remove #{filename}? (y/n)\n")
- if response.strip =~ /y/i
- logger.info "Removing #{filename}"
+ do_remove = yes?("Remove #{filename}?", :red)
+ if do_remove
+ say "Removing #{filename}"
filename.delete
removed += 1
else
- logger.info "Skipping #{filename}"
+ say "Skipping #{filename}", :yellow
end
end
- logger.info "Removed #{removed} files."
+ say "Removed #{removed} files."
else
- logger.info "No installed files matched."
+ say "No installed files matched."
end
end
- alias :uninstall :remove
+ map "uninstall" => "remove"
- # list the files and their resolved paths specified in the Jimfile
+ desc "resolve",
+ "Resolve all the paths listed in a Jimfile and print them to STDOUT. " +
+ "If no Jimfile is set in the options, assumes ./Jimfile."
def resolve
resolved = bundler.resolve!
- logger.info "Files:"
- resolved.each do |r|
- logger.info r.join(" | ")
+ say "Files:"
+ resolved.each do |bundle_name, requirements|
+ say bundle_name, :green
+ say "-----------------------", :green
+ requirements.each do |path, name, version|
+ say [name, version, path].join(" | ") + "\n"
+ end
end
resolved
+ rescue Jim::Error => e
+ say e.message, :red
end
- # vendor to dir, then bundle and compress the Jimfile contents
+ desc "pack [DIR]",
+ "Runs in order, vendor, bundle, compress. This command " +
+ "simplifies the common workflow of vendoring and re-bundling " +
+ "before committing or deploying changes to a project"
def pack(dir = nil)
- logger.info "packing the Jimfile for this project"
- vendor(dir)
- bundle
- compress
+ say "Packing the Jimfile for this project"
+ invoke :vendor, [dir]
+ invoke :bundle
+ invoke :compress
end
- private
- def parse_options(runtime_args)
- OptionParser.new("", 24, ' ') do |opts|
- opts.banner = "Usage: jim [options] [command] [args]"
+ desc "watch [DIR]",
+ "Watches your Jimfile and JS files and triggers `bundle` if something " +
+ "changes. Handy for development."
+ def watch(dir = nil)
+ require 'fssm'
+ run_update = lambda {|type, path|
+ unless bundler.bundle_paths.any? {|p| path.include?(p) }
+ say("--> #{path} #{type}")
+ system "jim bundle"
+ end
+ }
+ say "Now watching JS files..."
+ run_update["started", 'Jimfile']
+ FSSM.monitor(Dir.pwd, [File.join('**','*.js'), 'Jimfile']) do
+ update do |base, relative|
+ run_update["changed", relative]
+ end
- opts.separator ""
- opts.separator "jim options:"
+ create do |base, relative|
+ run_update["created", relative]
+ end
- opts.on("--jimhome path/to/home", "set the install path/JIMHOME dir (default ~/.jim)") {|h|
- self.jimhome = Pathname.new(h)
- }
-
- opts.on("-j", "--jimfile path/to/jimfile", "load specific Jimfile at path (default ./Jimfile)") { |j|
- self.jimfile = Pathname.new(j)
- }
-
- opts.on("-f", "--force", "force file creation/overwrite") {|f|
- self.force = true
- }
-
- opts.on("-d", "--debug", "set log level to debug") {|d|
- logger.level = Logger::DEBUG
- }
-
- opts.on("-o", "--stdout", "write output of commands (like bundle and compress to STDOUT)") {|o|
- logger.level = Logger::ERROR
- self.stdout = true
- }
-
- opts.on("-v", "--version", "print version") {|d|
- puts "jim #{Jim::VERSION}"
- exit
- }
-
-
- opts.on_tail("-h", "--help", "Show this message. Run jim commands for list of commands.") do
- puts opts.help
- exit
+ delete do |base, relative|
+ run_update["deleted", relative]
end
+ end
+ end
- end.parse! runtime_args
- rescue OptionParser::MissingArgument => e
- logger.warn "#{e}, run -h for options"
- exit
+ desc "update_jimfile [APP_DIR]",
+ "Converts a Jimfile from the old pre 0.3 format to the JSON format."
+ def update_jimfile(dir = nil)
+ dir = Pathname.new(dir || '')
+ bundler
+ copy_file(dir + 'Jimfile', dir + 'Jimfile.old')
+ create_file(dir + 'Jimfile', bundler.jimfile_to_json)
end
+ private
def index
@index ||= Jim::Index.new(install_dir, Dir.pwd)
end
def installed_index
@@ -221,21 +259,32 @@
def install_dir
jimhome + 'lib'
end
- def template(path)
- (Pathname.new(__FILE__).dirname + 'templates' + path).read
- end
-
def logger
Jim.logger
end
def print_version_list(list)
list.each do |file, versions|
- logger.info "#{file} (#{VersionSorter.rsort(versions.collect {|v| v[0] }).join(', ')})"
+ say "#{file} (#{VersionSorter.rsort(versions.collect {|v| v[0] }).join(', ')}\n"
end
+ end
+
+ def make_bundle(bundle_name, compress = false)
+ bundler.bundle_dir = options[:bundle_dir] if options[:bundle_dir]
+ bundler.bundle_dir = nil if options[:stdout]
+ result = bundler.bundle!(bundle_name, compress)
+ if options[:stdout]
+ puts result
+ else
+ result.each do |path|
+ say("Wrote #{path} #{File.size(path.to_s) / 1024}kb", :green)
+ end
+ end
+ rescue Jim::Error => e
+ say e.message, :red
end
end
end