Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor CLI & Commands For Greater Happiness #2143

Merged
merged 2 commits into from
Mar 16, 2014
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 4 additions & 88 deletions bin/jekyll
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,6 @@ require 'mercenary'

Jekyll::Deprecator.process(ARGV)

def add_build_options(c)
c.option 'config', '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'
c.option 'future', '--future', 'Publishes posts with a future date'
c.option 'limit_posts', '--limit_posts MAX_POSTS', Integer, 'Limits the number of posts to parse and publish'
c.option 'watch', '-w', '--watch', 'Watch for changes and rebuild'
c.option 'lsi', '--lsi', 'Use LSI for improved related posts'
c.option 'show_drafts', '-D', '--drafts', 'Render posts in the _drafts folder'
c.option 'quiet', '-q', '--quiet', 'Silence output.'
c.option 'verbose', '-V', '--verbose', 'Print verbose output.'
end

Mercenary.program(:jekyll) do |p|
p.version Jekyll::VERSION
p.description 'Jekyll is a blog-aware, static site generator in Ruby'
Expand All @@ -30,93 +19,20 @@ Mercenary.program(:jekyll) do |p|
p.option 'plugins', '-p', '--plugins PLUGINS_DIR1[,PLUGINS_DIR2[,...]]', Array, 'Plugins directory (defaults to ./_plugins)'
p.option 'layouts', '--layouts DIR', String, 'Layouts directory (defaults to ./_layouts)'

Jekyll::Command.subclasses.each { |c| c.init_with_program(p) }

p.action do |args, options|
if args.empty?
p.go(["-h"])
puts p
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@imathis This is how you print the command help. The p.go(['-h']) just results in a big ol' loop that never ends which sucks. If you just print the program, it'll print out the help message as you'd expect.

else
unless p.has_command?(args.first)
Jekyll.logger.abort_with "Invalid command. Use --help for more information"
end
end
end

p.command(:new) do |c|
c.syntax 'jekyll new PATH'
c.description 'Creates a new Jekyll site scaffold in PATH'

c.option 'force', '--force', 'Force creation even if PATH already exists'
c.option 'blank', '--blank', 'Creates scaffolding but with empty files'

c.action do |args, options|
Jekyll::Commands::New.process(args)
end
end

p.command(:build) do |c|
c.syntax 'jekyll build [options]'
c.description 'Build your site'

add_build_options(c)

c.action do |args, options|
options["serving"] = false
config = Jekyll.configuration(options)
Jekyll::Commands::Build.process(config)
end
end

p.command(:serve) do |c|
c.syntax 'jekyll serve [options]'
c.description 'Serve your site locally'
c.alias :server

add_build_options(c)

c.option 'detach', '-B', '--detach', 'Run the server in the background (detach)'
c.option 'port', '-P', '--port [PORT]', 'Port to listen on'
c.option 'host', '-H', '--host [HOST]', 'Host to bind to'
c.option 'baseurl', '-b', '--baseurl [URL]', 'Base URL'

c.action do |args, options|
options["serving"] ||= true
options = Jekyll.configuration(options)
Jekyll::Commands::Build.process(options)
Jekyll::Commands::Serve.process(options)
end
end

p.command(:doctor) do |c|
c.syntax 'jekyll doctor'
c.description 'Search site and print specific deprecation warnings'
c.alias(:hyde)

c.option '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'

c.action do |args, options|
options = Jekyll.configuration(options)
Jekyll::Commands::Doctor.process(options)
end
end

p.command(:docs) do |c|
c.syntax 'jekyll docs'
c.description "Launch local server with docs for Jekyll v#{Jekyll::VERSION}"

c.option 'port', '-P', '--port [PORT]', 'Port to listen on'
c.option 'host', '-H', '--host [HOST]', 'Host to bind to'

c.action do |args, options|
options = Jekyll.configuration(options.merge!({
'source' => File.expand_path("../site", File.dirname(__FILE__)),
'destination' => File.expand_path("../site/_site", File.dirname(__FILE__))
}))
Jekyll::Commands::Build.process(options)
Jekyll::Commands::Serve.process(options)
end
end

p.command(:import) do |c|
c.syntax 'jekyll import <platform> [options]'
c.syntax 'import <platform> [options]'
c.description 'Import your old blog to Jekyll'
importers = []

Expand Down
92 changes: 72 additions & 20 deletions lib/jekyll/command.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,79 @@
module Jekyll
class Command
def self.globs(source, destination)
Dir.chdir(source) do
dirs = Dir['*'].select { |x| File.directory?(x) }
dirs -= [destination, File.expand_path(destination), File.basename(destination)]
dirs = dirs.map { |x| "#{x}/**/*" }
dirs += ['*']

class << self

# A list of subclasses of Jekyll::Command
def subclasses
@subclasses ||= []
end

# Keep a list of subclasses of Jekyll::Command every time it's inherited
# Called automatically.
#
# base - the subclass
#
# Returns nothing
def inherited(base)
subclasses << base
super(base)
end

# Listing of all directories (globbed to include subfiles and folders)
#
# source - the source path
# destination - the destination path
#
# Returns an Array of directory globs in the source, excluding the destination
def globs(source, destination)
Dir.chdir(source) do
dirs = Dir['*'].select { |x| File.directory?(x) }
dirs -= [destination, File.expand_path(destination), File.basename(destination)]
dirs = dirs.map { |x| "#{x}/**/*" }
dirs += ['*']
end
end

# Run Site#process and catch errors
#
# site - the Jekyll::Site object
#
# Returns nothing
def process_site(site)
site.process
rescue Jekyll::FatalException => e
Jekyll.logger.error "ERROR:", "YOUR SITE COULD NOT BE BUILT:"
Jekyll.logger.error "", "------------------------------------"
Jekyll.logger.error "", e.message
exit(1)
end

# Create a full Jekyll configuration with the options passed in as overrides
#
# options - the configuration overrides
#
# Returns a full Jekyll configuration
def configuration_from_options(options)
Jekyll.configuration(options)
end

# Add common options to a command for building configuration
#
# c - the Jekyll::Command to add these options to
#
# Returns nothing
def add_build_options(c)
c.option 'config', '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'
c.option 'future', '--future', 'Publishes posts with a future date'
c.option 'limit_posts', '--limit_posts MAX_POSTS', Integer, 'Limits the number of posts to parse and publish'
c.option 'watch', '-w', '--watch', 'Watch for changes and rebuild'
c.option 'lsi', '--lsi', 'Use LSI for improved related posts'
c.option 'show_drafts', '-D', '--drafts', 'Render posts in the _drafts folder'
c.option 'quiet', '-q', '--quiet', 'Silence output.'
c.option 'verbose', '-V', '--verbose', 'Print verbose output.'
end
end

# Static: Run Site#process and catch errors
#
# site - the Jekyll::Site object
#
# Returns nothing
def self.process_site(site)
site.process
rescue Jekyll::FatalException => e
puts
Jekyll.logger.error "ERROR:", "YOUR SITE COULD NOT BE BUILT:"
Jekyll.logger.error "", "------------------------------------"
Jekyll.logger.error "", e.message
exit(1)
end

end
end
138 changes: 81 additions & 57 deletions lib/jekyll/commands/build.rb
Original file line number Diff line number Diff line change
@@ -1,72 +1,96 @@
module Jekyll
module Commands
class Build < Command
def self.process(options)
site = Jekyll::Site.new(options)

Jekyll.logger.log_level = Jekyll::Stevenson::ERROR if options['quiet']

build(site, options)
watch(site, options) if options['watch']
end

# Private: Build the site from source into destination.
#
# site - A Jekyll::Site instance
# options - A Hash of options passed to the command
#
# Returns nothing.
def self.build(site, options)
source = options['source']
destination = options['destination']
Jekyll.logger.info "Source:", source
Jekyll.logger.info "Destination:", destination
print Jekyll.logger.formatted_topic "Generating..."
process_site(site)
puts "done."
end

# Private: Watch for file changes and rebuild the site.
#
# site - A Jekyll::Site instance
# options - A Hash of options passed to the command
#
# Returns nothing.
def self.watch(site, options)
require 'listen'

source = options['source']
destination = options['destination']

begin
dest = Pathname.new(destination).relative_path_from(Pathname.new(source)).to_s
ignored = Regexp.new(Regexp.escape(dest))
rescue ArgumentError
# Destination is outside the source, no need to ignore it.
ignored = nil

class << self

# Create the Mercenary command for the Jekyll CLI for this Command
def init_with_program(prog)
prog.command(:build) do |c|
c.syntax 'build [options]'
c.description 'Build your site'

add_build_options(c)

c.action do |args, options|
options["serving"] = false
Jekyll::Commands::Build.process(options)
end
end
end

Jekyll.logger.info "Auto-regeneration:", "enabled"
# Build your jekyll site
# Continuously watch if `watch` is set to true in the config.
def process(options)
options = configuration_from_options(options)
site = Jekyll::Site.new(options)

listener = Listen.to(source, :ignore => ignored) do |modified, added, removed|
t = Time.now.strftime("%Y-%m-%d %H:%M:%S")
n = modified.length + added.length + removed.length
print Jekyll.logger.formatted_topic("Regenerating:") + "#{n} files at #{t} "
Jekyll.logger.log_level = Jekyll::Stevenson::ERROR if options['quiet']

build(site, options)
watch(site, options) if options['watch']
end

# Build your Jekyll site.
#
# site - the Jekyll::Site instance to build
# options - the
#
# Returns nothing.
def build(site, options)
source = options['source']
destination = options['destination']
Jekyll.logger.info "Source:", source
Jekyll.logger.info "Destination:", destination
Jekyll.logger.info "Generating..."
process_site(site)
puts "...done."
Jekyll.logger.info "", "done."
end
listener.start

unless options['serving']
trap("INT") do
listener.stop
puts " Halting auto-regeneration."
exit 0
# Private: Watch for file changes and rebuild the site.
#
# site - A Jekyll::Site instance
# options - A Hash of options passed to the command
#
# Returns nothing.
def watch(site, options)
require 'listen'

source = options['source']
destination = options['destination']

begin
dest = Pathname.new(destination).relative_path_from(Pathname.new(source)).to_s
ignored = Regexp.new(Regexp.escape(dest))
rescue ArgumentError
# Destination is outside the source, no need to ignore it.
ignored = nil
end

Jekyll.logger.info "Auto-regeneration:", "enabled"

listener = Listen.to(source, :ignore => ignored) do |modified, added, removed|
t = Time.now.strftime("%Y-%m-%d %H:%M:%S")
n = modified.length + added.length + removed.length
print Jekyll.logger.formatted_topic("Regenerating:") + "#{n} files at #{t} "
process_site(site)
puts "...done."
end
listener.start

loop { sleep 1000 }
unless options['serving']
trap("INT") do
listener.stop
puts " Halting auto-regeneration."
exit 0
end

loop { sleep 1000 }
end
end
end

end # end of class << self

end
end
end
Loading