Update CLI

This commit is contained in:
Thomas Reynolds 2015-01-04 14:23:35 -06:00
parent 302a891bbb
commit f16510d034
13 changed files with 193 additions and 269 deletions

View file

@ -1,6 +1,7 @@
master
===
* Remove side-loading of CLI tasks from `tasks/`
* Add the option of naming `config.rb` as `middleman.rb`.
* Builder extracted from Thor. `after_build` hook now passes an instance of a Builder instead of the Thor CLI.
* New FileWatcher API.

View file

@ -14,5 +14,10 @@ require "middleman-cli"
# Change directory to the root
Dir.chdir(ENV["MM_ROOT"]) if ENV["MM_ROOT"]
# Default command is server
if ARGV[0] != 'help' && (ARGV.length < 1 || ARGV.first.include?('-'))
ARGV.unshift('server')
end
# Start the CLI
Middleman::Cli::Base.start
Middleman::Cli::Base.start(ARGV)

View file

@ -6,81 +6,18 @@ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
# Require Thor since that's what the whole CLI is built around
require 'thor'
require 'thor/group'
# CLI Module
module Middleman
module Cli
# The base task from which everything else extends
class Base < Thor
class << self
def start(*args)
# Change flag to a module
ARGV.unshift('help') if ARGV.delete('--help')
module Middleman::Cli
# The base task from which everything else extends
class Base < ::Thor
desc 'version', 'Show version'
def version
say "Middleman #{Middleman::VERSION}"
end
# Default command is server
if ARGV[0] != 'help' && (ARGV.length < 1 || ARGV.first.include?('-'))
ARGV.unshift('server')
end
super
end
end
desc 'version', 'Show version'
def version
say "Middleman #{Middleman::VERSION}"
end
desc 'help', 'Show help'
# Override the Thor help method to find help for subtasks
# @param [Symbol, String, nil] meth
# @param [Boolean] subcommand
# @return [void]
# rubocop:disable UnusedMethodArgument
def help(meth=nil, subcommand=false)
if meth && !self.respond_to?(meth)
klass, task = Thor::Util.find_class_and_task_by_namespace("#{meth}:#{meth}")
klass.start(['-h', task].compact, shell: shell)
else
list = []
Thor::Util.thor_classes_in(Middleman::Cli).each do |thor_class|
list += thor_class.printable_tasks(false)
end
list.sort! { |a, b| a[0] <=> b[0] }
shell.say 'Tasks:'
shell.print_table(list, ident: 2, truncate: true)
shell.say %(\nSee 'middleman help <command>' for more information on specific command.)
shell.say
end
end
# Intercept missing methods and search subtasks for them
# @param [Symbol] meth
def method_missing(meth, *args)
meth = meth.to_s
meth = self.class.map[meth] if self.class.map.key?(meth)
klass, task = Thor::Util.find_class_and_task_by_namespace("#{meth}:#{meth}")
if klass.nil?
tasks_dir = File.join(Dir.pwd, 'tasks')
if File.exist?(tasks_dir)
Dir[File.join(tasks_dir, '**/*_task.rb')].each { |f| require f }
klass, task = Thor::Util.find_class_and_task_by_namespace("#{meth}:#{meth}")
end
end
if klass.nil?
raise Thor::Error, "There's no '#{meth}' command for Middleman. Try 'middleman help' for a list of commands."
else
args.unshift(task) if task
klass.start(args, shell: shell)
end
end
def self.exit_on_failure?
true
end
end
end

View file

@ -1,39 +1,36 @@
# CLI Module
module Middleman::Cli
# The CLI Build class
class Build < Thor
class Build < Thor::Group
include Thor::Actions
check_unknown_options!
namespace :build
desc 'build [options]', 'Builds the static site for deployment'
method_option :environment,
aliases: '-e',
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'production',
desc: 'The environment Middleman will run under'
method_option :clean,
type: :boolean,
default: true,
desc: 'Remove orphaned files from build (--no-clean to disable)'
method_option :glob,
type: :string,
aliases: '-g',
default: nil,
desc: 'Build a subset of the project'
method_option :verbose,
type: :boolean,
default: false,
desc: 'Print debug messages'
method_option :instrument,
type: :string,
default: false,
desc: 'Print instrument messages'
method_option :profile,
type: :boolean,
default: false,
desc: 'Generate profiling report for the build'
class_option :environment,
aliases: '-e',
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'production',
desc: 'The environment Middleman will run under'
class_option :clean,
type: :boolean,
default: true,
desc: 'Remove orphaned files from build (--no-clean to disable)'
class_option :glob,
type: :string,
aliases: '-g',
default: nil,
desc: 'Build a subset of the project'
class_option :verbose,
type: :boolean,
default: false,
desc: 'Print debug messages'
class_option :instrument,
type: :string,
default: false,
desc: 'Print instrument messages'
class_option :profile,
type: :boolean,
default: false,
desc: 'Generate profiling report for the build'
# Core build Thor command
# @return [void]
@ -78,51 +75,49 @@ module Middleman::Cli
end
end
# Tell Thor to send an exit status on a failure.
def self.exit_on_failure?
true
end
protected
no_tasks do
# Handles incoming events from the builder.
# @param [Symbol] event_type The type of event.
# @param [String] contents The event contents.
# @param [String] extra The extra information.
# @return [void]
def on_event(event_type, target, extra=nil)
case event_type
when :error
say_status :error, target, :red
shell.say extra, :red if options['verbose']
when :deleted
say_status :remove, target, :green
when :created
say_status :create, target, :green
when :identical
say_status :identical, target, :blue
when :updated
say_status :updated, target, :yellow
else
say_status event_type, extra, :blue
end
end
# Find empty directories in the build folder and remove them.
# @return [Boolean]
def clean_directories!
all_build_files = File.join(@app.config[:build_dir], '**', '*')
empty_directories = Dir[all_build_files].select do |d|
File.directory?(d)
end
empty_directories.each do |d|
remove_file d, force: true if Pathname(d).children.empty?
end
# Handles incoming events from the builder.
# @param [Symbol] event_type The type of event.
# @param [String] contents The event contents.
# @param [String] extra The extra information.
# @return [void]
def on_event(event_type, target, extra=nil)
case event_type
when :error
say_status :error, target, :red
shell.say extra, :red if options['verbose']
when :deleted
say_status :remove, target, :green
when :created
say_status :create, target, :green
when :identical
say_status :identical, target, :blue
when :updated
say_status :updated, target, :yellow
else
say_status event_type, extra, :blue
end
end
# Find empty directories in the build folder and remove them.
# @return [Boolean]
def clean_directories!
all_build_files = File.join(@app.config[:build_dir], '**', '*')
empty_directories = Dir[all_build_files].select do |d|
File.directory?(d)
end
empty_directories.each do |d|
remove_file d, force: true if Pathname(d).children.empty?
end
end
# Add to CLI
Base.register(self, 'build', 'build [options]', 'Builds the static site for deployment')
# Map "b" to "build"
Base.map('b' => 'build')
end
# Alias "b" to "build"
Base.map('b' => 'build')
end

View file

@ -1,25 +1,19 @@
# CLI Module
module Middleman::Cli
# Alias "c" to "console"
Base.map(c: 'console')
# The CLI Console class
class Console < Thor
class Console < Thor::Group
include Thor::Actions
check_unknown_options!
namespace :console
desc 'console [options]', 'Start an interactive console in the context of your Middleman application'
method_option :environment,
aliases: '-e',
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
desc: 'The environment Middleman will run under'
method_option :verbose,
type: :boolean,
default: false,
desc: 'Print debug messages'
class_option :environment,
aliases: '-e',
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
desc: 'The environment Middleman will run under'
class_option :verbose,
type: :boolean,
default: false,
desc: 'Print debug messages'
def console
require 'middleman-core'
require 'irb'
@ -42,5 +36,11 @@ module Middleman::Cli
require 'irb/ext/multi-irb'
IRB.irb nil, @app
end
# Add to CLI
Base.register(self, 'console', 'console [options]', 'Start an interactive console in the context of your Middleman application')
# Map "c" to "console"
Base.map('c' => 'console')
end
end

View file

@ -1,13 +1,11 @@
# CLI Module
module Middleman::Cli
# A thor task for creating new projects
class Extension < Thor
class Extension < Thor::Group
include Thor::Actions
check_unknown_options!
namespace :extension
# Required path for the new project to be generated
argument :name, type: :string
@ -17,11 +15,13 @@ module Middleman::Cli
File.join(File.dirname(__FILE__), 'templates')
end
desc 'extension [options]', 'Create Middleman extension scaffold NAME'
method_option 'skip-git',
type: :boolean,
default: false,
desc: 'Skip Git ignores and keeps'
class_option 'skip-git',
type: :boolean,
default: false,
desc: 'Skip Git ignores and keeps'
# Output a .gitignore file
class_option :git, type: :boolean, default: true
# The extension task
# @param [String] name
@ -36,7 +36,7 @@ module Middleman::Cli
empty_directory File.join(name, 'fixtures')
end
# Output a .gitignore file
class_option :git, type: :boolean, default: true
# Add to CLI
Base.register(self, 'extension', 'extension [options]', 'Create a new Middleman extension')
end
end

View file

@ -1,28 +1,27 @@
# CLI Module
module Middleman::Cli
# A thor task for creating new projects
class Init < Thor
class Init < Thor::Group
include Thor::Actions
check_unknown_options!
namespace :init
argument :target, type: :string, default: '.'
desc 'init TARGET [options]', 'Create new project at TARGET'
method_option 'template',
aliases: '-T',
default: 'middleman/middleman-templates-default',
desc: 'Use a project template'
class_option 'template',
aliases: '-T',
default: 'middleman/middleman-templates-default',
desc: 'Use a project template'
# Do not run bundle install
method_option 'skip-bundle',
type: :boolean,
aliases: '-B',
default: false,
desc: 'Skip bundle install'
class_option 'skip-bundle',
type: :boolean,
aliases: '-B',
default: false,
desc: 'Skip bundle install'
# The init task
# @param [String] name
def init(target='.')
def init
require 'tmpdir'
repo_path, repo_branch = if shortname?(options[:template])
@ -37,8 +36,8 @@ module Middleman::Cli
data['links']['github']
data['links']['github'].split('#')
rescue ::OpenURI::HTTPError
puts "Template `#{options[:template]}` not found in Middleman Directory."
puts 'Did you mean to use a full `user/repo` path?'
say "Template `#{options[:template]}` not found in Middleman Directory."
say 'Did you mean to use a full `user/repo` path?'
exit
end
else
@ -51,13 +50,20 @@ module Middleman::Cli
run("git #{cmd} #{repo_path} #{dir}")
source_paths << dir
directory dir, target, exclude_pattern: /\.git\/|\.gitignore$/
inside(target) do
run('bundle install')
end unless ENV['TEST'] || options[:'skip-bundle']
thorfile = File.join(dir, 'Thorfile')
if File.exist?(thorfile)
::Thor::Util.load_thorfile(thorfile)
invoke 'middleman:generator'
else
source_paths << dir
directory dir, '.', exclude_pattern: /\.git\/|\.gitignore$/
end
run('bundle install') unless ENV['TEST'] || options[:'skip-bundle']
end
end
end
@ -70,16 +76,15 @@ module Middleman::Cli
def repository_path(repo)
"git://github.com/#{repo}.git"
end
end
def self.exit_on_failure?
true
end
# Add to CLI
Base.register(self, 'init', 'init TARGET [options]', 'Create new project at TARGET')
# Map "i", "new" and "n" to "init"
Base.map(
'i' => 'init',
'new' => 'init',
'n' => 'init'
)
# Map "i", "new" and "n" to "init"
Base.map(
'i' => 'init',
'new' => 'init',
'n' => 'init'
)
end
end

View file

@ -1,50 +1,47 @@
# CLI Module
module Middleman::Cli
# Server thor task
class Server < Thor
class Server < Thor::Group
check_unknown_options!
namespace :server
desc 'server [options]', 'Start the preview server'
method_option :environment,
aliases: '-e',
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
desc: 'The environment Middleman will run under'
method_option :host,
type: :string,
aliases: '-h',
default: '0.0.0.0',
desc: 'Bind to HOST address'
method_option :port,
aliases: '-p',
default: '4567',
desc: 'The port Middleman will listen on'
method_option :verbose,
type: :boolean,
default: false,
desc: 'Print debug messages'
method_option :instrument,
type: :string,
default: false,
desc: 'Print instrument messages'
method_option :disable_watcher,
type: :boolean,
default: false,
desc: 'Disable the file change and delete watcher process'
method_option :profile,
type: :boolean,
default: false,
desc: 'Generate profiling report for server startup'
method_option :force_polling,
type: :boolean,
default: false,
desc: 'Force file watcher into polling mode'
method_option :latency,
type: :numeric,
aliases: '-l',
default: 0.25,
desc: 'Set file watcher latency, in seconds'
class_option :environment,
aliases: '-e',
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
desc: 'The environment Middleman will run under'
class_option :host,
type: :string,
aliases: '-h',
default: '0.0.0.0',
desc: 'Bind to HOST address'
class_option :port,
aliases: '-p',
default: '4567',
desc: 'The port Middleman will listen on'
class_option :verbose,
type: :boolean,
default: false,
desc: 'Print debug messages'
class_option :instrument,
type: :string,
default: false,
desc: 'Print instrument messages'
class_option :disable_watcher,
type: :boolean,
default: false,
desc: 'Disable the file change and delete watcher process'
class_option :profile,
type: :boolean,
default: false,
desc: 'Generate profiling report for server startup'
class_option :force_polling,
type: :boolean,
default: false,
desc: 'Force file watcher into polling mode'
class_option :latency,
type: :numeric,
aliases: '-l',
default: 0.25,
desc: 'Set file watcher latency, in seconds'
# Start the server
def server
@ -71,12 +68,11 @@ module Middleman::Cli
puts '== The Middleman is loading'
::Middleman::PreviewServer.start(params)
end
end
def self.exit_on_failure?
true
end
# Add to CLI
Base.register(self, 'server', 'server [options]', 'Start the preview server')
# Map "s" to "server"
Base.map('s' => 'server')
# Map "s" to "server"
Base.map('s' => 'server')
end
end

View file

@ -24,7 +24,7 @@ class MyExtension < ::Middleman::Extension
# def manipulate_resource_list(resources)
# end
# module do
# helpers do
# def a_helper
# end
# end

View file

@ -1,15 +0,0 @@
Feature: Allow config.rb and extensions to add CLI commands
Scenario: Command autoloaded from tasks/ directory
Given an empty app
And a file named "tasks/hello_task.rb" with:
"""
class Hello < Thor
desc "hello", "Say hello"
def hello
puts "Hello World"
end
end
"""
When I run `middleman hello`
Then the output should contain "Hello World"

View file

@ -17,5 +17,5 @@ end
World(ArubaMonkeypatch)
Before do
@aruba_timeout_seconds = RUBY_PLATFORM == 'java' ? 120 : 60
@aruba_timeout_seconds = RUBY_PLATFORM == 'java' ? 240 : 120
end