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 master
=== ===
* Remove side-loading of CLI tasks from `tasks/`
* Add the option of naming `config.rb` as `middleman.rb`. * 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. * Builder extracted from Thor. `after_build` hook now passes an instance of a Builder instead of the Thor CLI.
* New FileWatcher API. * New FileWatcher API.

View file

@ -14,5 +14,10 @@ require "middleman-cli"
# Change directory to the root # Change directory to the root
Dir.chdir(ENV["MM_ROOT"]) if ENV["MM_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 # 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 since that's what the whole CLI is built around
require 'thor' require 'thor'
require 'thor/group'
# CLI Module # CLI Module
module Middleman module Middleman::Cli
module Cli # The base task from which everything else extends
# The base task from which everything else extends class Base < ::Thor
class Base < Thor desc 'version', 'Show version'
class << self def version
def start(*args) say "Middleman #{Middleman::VERSION}"
# Change flag to a module end
ARGV.unshift('help') if ARGV.delete('--help')
# Default command is server def self.exit_on_failure?
if ARGV[0] != 'help' && (ARGV.length < 1 || ARGV.first.include?('-')) true
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
end end
end end
end end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -24,7 +24,7 @@ class MyExtension < ::Middleman::Extension
# def manipulate_resource_list(resources) # def manipulate_resource_list(resources)
# end # end
# module do # helpers do
# def a_helper # def a_helper
# end # end
# 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) World(ArubaMonkeypatch)
Before do Before do
@aruba_timeout_seconds = RUBY_PLATFORM == 'java' ? 120 : 60 @aruba_timeout_seconds = RUBY_PLATFORM == 'java' ? 240 : 120
end end