jekyll-compose-0.12.0/0000755000004100000410000000000013602504365014563 5ustar www-datawww-datajekyll-compose-0.12.0/jekyll-compose.gemspec0000644000004100000410000000515113602504365021067 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: jekyll-compose 0.12.0 ruby lib Gem::Specification.new do |s| s.name = "jekyll-compose".freeze s.version = "0.12.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Parker Moore".freeze] s.date = "2019-12-28" s.description = "Streamline your writing in Jekyll with these commands.".freeze s.email = ["parkrmoore@gmail.com".freeze] s.files = ["lib/jekyll-compose.rb".freeze, "lib/jekyll-compose/arg_parser.rb".freeze, "lib/jekyll-compose/file_creator.rb".freeze, "lib/jekyll-compose/file_editor.rb".freeze, "lib/jekyll-compose/file_info.rb".freeze, "lib/jekyll-compose/file_mover.rb".freeze, "lib/jekyll-compose/movement_arg_parser.rb".freeze, "lib/jekyll-compose/version.rb".freeze, "lib/jekyll/commands/compose.rb".freeze, "lib/jekyll/commands/draft.rb".freeze, "lib/jekyll/commands/page.rb".freeze, "lib/jekyll/commands/post.rb".freeze, "lib/jekyll/commands/publish.rb".freeze, "lib/jekyll/commands/rename.rb".freeze, "lib/jekyll/commands/unpublish.rb".freeze] s.homepage = "https://github.com/jekyll/jekyll-compose".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.4.0".freeze) s.rubygems_version = "2.5.2.1".freeze s.summary = "Streamline your writing in Jekyll with these commands.".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q.freeze, [">= 0"]) s.add_runtime_dependency(%q.freeze, ["< 5.0", ">= 3.7"]) s.add_development_dependency(%q.freeze, ["~> 12.0"]) s.add_development_dependency(%q.freeze, ["~> 3.0"]) s.add_development_dependency(%q.freeze, ["~> 0.5"]) else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["< 5.0", ">= 3.7"]) s.add_dependency(%q.freeze, ["~> 12.0"]) s.add_dependency(%q.freeze, ["~> 3.0"]) s.add_dependency(%q.freeze, ["~> 0.5"]) end else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["< 5.0", ">= 3.7"]) s.add_dependency(%q.freeze, ["~> 12.0"]) s.add_dependency(%q.freeze, ["~> 3.0"]) s.add_dependency(%q.freeze, ["~> 0.5"]) end end jekyll-compose-0.12.0/lib/0000755000004100000410000000000013602504365015331 5ustar www-datawww-datajekyll-compose-0.12.0/lib/jekyll/0000755000004100000410000000000013602504365016623 5ustar www-datawww-datajekyll-compose-0.12.0/lib/jekyll/commands/0000755000004100000410000000000013602504365020424 5ustar www-datawww-datajekyll-compose-0.12.0/lib/jekyll/commands/rename.rb0000644000004100000410000000703413602504365022224 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Commands class Rename < Command def self.init_with_program(prog) prog.command(:rename) do |c| c.syntax "rename PATH NAME" c.description "Moves a file to a given NAME and sets the title and date" options.each { |opt| c.option(*opt) } c.action { |args, options| process(args, options) } end end def self.options [ ["force", "-f", "--force", "Overwrite a post if it already exists"], ["config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array, "Custom configuration file"], ["date", "-d DATE", "--date DATE", "Specify the date"], ["now", "--now", "Specify the date as now"], ] end def self.process(args = [], options = {}) config = configuration_from_options(options) params = RenameArgParser.new(args, options, config) params.validate! movement = RenameMovementInfo.new(params) mover = RenameMover.new(movement, params.force?, params.source) mover.move end end class RenameArgParser < Compose::ArgParser def validate! raise ArgumentError, "You must specify current path and the new title." if args.length < 2 if options.values_at("date", "now").compact.length > 1 raise ArgumentError, "You can only specify one of --date DATE or --now." end end def current_path @current_path ||= args[0] end def path File.join(source, current_path).sub(%r!\A/!, "") end def dirname @dirname ||= File.dirname(current_path) end def basename @basename ||= File.basename(current_path) end def title @title ||= args.drop(1).join(" ") end def touch? !!options["date"] || options["now"] end def date @date ||= if options["now"] Time.now elsif options["date"] Date.parse(options["date"]) end end def date_from_filename Date.parse(Regexp.last_match(1)) if basename =~ Jekyll::Document::DATE_FILENAME_MATCHER end def post? dirname == "_posts" end def draft? dirname == "_drafts" end end class RenameMovementInfo < Compose::FileInfo attr_reader :params def initialize(params) @params = params end def from params.path end def resource_type if @params.post? "post" elsif @params.draft? "draft" else "file" end end def to if @params.post? File.join(@params.dirname, "#{date_stamp}-#{file_name}") else File.join(@params.dirname, file_name) end end def front_matter(data) data["title"] = params.title data["date"] = time_stamp if @params.touch? data end private def date_stamp if @params.touch? @params.date.strftime Jekyll::Compose::DEFAULT_DATESTAMP_FORMAT else @params.date_from_filename.strftime Jekyll::Compose::DEFAULT_DATESTAMP_FORMAT end end def time_stamp @params.date.strftime Jekyll::Compose::DEFAULT_TIMESTAMP_FORMAT end end class RenameMover < Compose::FileMover def resource_type_from @movement.resource_type end alias_method :resource_type_to, :resource_type_from end end end jekyll-compose-0.12.0/lib/jekyll/commands/draft.rb0000644000004100000410000000331213602504365022050 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Commands class Draft < Command def self.init_with_program(prog) prog.command(:draft) do |c| c.syntax "draft NAME" c.description "Creates a new draft post with the given NAME" options.each { |opt| c.option(*opt) } c.action { |args, options| process(args, options) } end end def self.options [ ["extension", "-x EXTENSION", "--extension EXTENSION", "Specify the file extension"], ["layout", "-l LAYOUT", "--layout LAYOUT", "Specify the draft layout"], ["force", "-f", "--force", "Overwrite a draft if it already exists"], ["config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array, "Custom configuration file"], ] end def self.process(args = [], options = {}) config = configuration_from_options(options) params = Compose::ArgParser.new(args, options, config) params.validate! draft = DraftFileInfo.new(params) file_creator = Compose::FileCreator.new(draft, params.force?, params.source) file_creator.create! Compose::FileEditor.bootstrap(config) Compose::FileEditor.open_editor(file_creator.file_path) end class DraftFileInfo < Compose::FileInfo def resource_type "draft" end def path "_drafts/#{file_name}" end def content(custom_front_matter = {}) default_front_matter = front_matter_defaults_for("drafts") custom_front_matter.merge!(default_front_matter) if default_front_matter.is_a?(Hash) super(custom_front_matter) end end end end end jekyll-compose-0.12.0/lib/jekyll/commands/page.rb0000644000004100000410000000264313602504365021672 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Commands class Page < Command def self.init_with_program(prog) prog.command(:page) do |c| c.syntax "page NAME" c.description "Creates a new page with the given NAME" options.each { |opt| c.option(*opt) } c.action { |args, options| process(args, options) } end end def self.options [ ["extension", "-x EXTENSION", "--extension EXTENSION", "Specify the file extension"], ["layout", "-l LAYOUT", "--layout LAYOUT", "Specify the page layout"], ["force", "-f", "--force", "Overwrite a page if it already exists"], ["config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array, "Custom configuration file"], ] end def self.process(args = [], options = {}) config = configuration_from_options(options) params = PageArgParser.new(args, options, config) params.validate! page = PageFileInfo.new(params) Compose::FileCreator.new(page, params.force?, params.source).create! end class PageArgParser < Compose::ArgParser def layout options["layout"] || Jekyll::Compose::DEFAULT_LAYOUT_PAGE end end class PageFileInfo < Compose::FileInfo def resource_type "page" end alias_method :path, :file_name end end end end jekyll-compose-0.12.0/lib/jekyll/commands/compose.rb0000644000004100000410000000642213602504365022422 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Commands class ComposeCommand < Command def self.init_with_program(prog) prog.command(:compose) do |c| c.syntax "compose NAME" c.description "Creates a new document with the given NAME" options.each { |opt| c.option(*opt) } c.action { |args, options| process(args, options) } end end def self.options [ ["extension", "-x EXTENSION", "--extension EXTENSION", "Specify the file extension"], ["layout", "-l LAYOUT", "--layout LAYOUT", "Specify the document layout"], ["force", "-f", "--force", "Overwrite a document if it already exists"], ["date", "-d DATE", "--date DATE", "Specify the document date"], ["collection", "-c COLLECTION", "--collection COLLECTION", "Specify the document collection"], ["post", "--post", "Create a new post (default)"], ["draft", "--draft", "Create a new draft"], ["config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array, "Custom configuration file"], ] end def self.process(args = [], options = {}) config = configuration_from_options(options) params = ComposeCommandArgParser.new(args, options, config) params.validate! document = ComposeCommandFileInfo.new(params) file_creator = Compose::FileCreator.new(document, params.force?, params.source) file_creator.create! Compose::FileEditor.bootstrap(config) Compose::FileEditor.open_editor(file_creator.file_path) end class ComposeCommandArgParser < Compose::ArgParser def validate! if options.values_at("post", "draft", "collection").compact.length > 1 raise ArgumentError, "You can only specify one of --post, --draft, or --collection COLLECTION." end super end def date @date ||= options["date"] ? Date.parse(options["date"]) : Time.now end def collection if (coll = options["collection"]) coll elsif options["draft"] "drafts" else "posts" end end end class ComposeCommandFileInfo < Compose::FileInfo def initialize(params) @params = params @collection = params.collection end def resource_type case @collection when "posts" then "post" when "drafts" then "draft" else "file" end end def path File.join("_#{@collection}", file_name) end def file_name @collection == "posts" ? "#{date_stamp}-#{super}" : super end def content(custom_front_matter = {}) default_front_matter = front_matter_defaults_for(@collection) custom_front_matter.merge!(default_front_matter) if default_front_matter.is_a?(Hash) super({ "date" => time_stamp }.merge!(custom_front_matter)) end private def date_stamp @params.date.strftime(Jekyll::Compose::DEFAULT_DATESTAMP_FORMAT) end def time_stamp @params.date.strftime(Jekyll::Compose::DEFAULT_TIMESTAMP_FORMAT) end end end end end jekyll-compose-0.12.0/lib/jekyll/commands/publish.rb0000644000004100000410000000400413602504365022415 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Commands class Publish < Command def self.init_with_program(prog) prog.command(:publish) do |c| c.syntax "publish DRAFT_PATH" c.description "Moves a draft into the _posts directory and sets the date" options.each { |opt| c.option(*opt) } c.action { |args, options| process(args, options) } end end def self.options [ ["date", "-d DATE", "--date DATE", "Specify the post date"], ["config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array, "Custom configuration file"], ["force", "-f", "--force", "Overwrite a post if it already exists"], ["timestamp_format", "--timestamp-format FORMAT", "Custom timestamp format"], ] end def self.process(args = [], options = {}) config = configuration_from_options(options) params = PublishArgParser.new args, options, config params.validate! movement = DraftMovementInfo.new params mover = DraftMover.new movement, params.force?, params.source mover.move end end class PublishArgParser < Compose::MovementArgParser def resource_type "draft" end def date @date ||= options["date"] ? Date.parse(options["date"]) : Time.now end def name File.basename path end end class DraftMovementInfo attr_reader :params def initialize(params) @params = params end def from params.path end def to date_stamp = params.date.strftime Jekyll::Compose::DEFAULT_DATESTAMP_FORMAT "_posts/#{date_stamp}-#{params.name}" end def front_matter(data) data["date"] ||= params.date.strftime(params.timestamp_format) data end end class DraftMover < Compose::FileMover def resource_type_from "draft" end def resource_type_to "post" end end end end jekyll-compose-0.12.0/lib/jekyll/commands/post.rb0000644000004100000410000000445313602504365021744 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Commands class Post < Command def self.init_with_program(prog) prog.command(:post) do |c| c.syntax "post NAME" c.description "Creates a new post with the given NAME" options.each { |opt| c.option(*opt) } c.action { |args, options| process(args, options) } end end def self.options [ ["extension", "-x EXTENSION", "--extension EXTENSION", "Specify the file extension"], ["layout", "-l LAYOUT", "--layout LAYOUT", "Specify the post layout"], ["force", "-f", "--force", "Overwrite a post if it already exists"], ["date", "-d DATE", "--date DATE", "Specify the post date"], ["config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array, "Custom configuration file"], ["timestamp_format", "--timestamp-format FORMAT", "Custom timestamp format"], ] end def self.process(args = [], options = {}) config = configuration_from_options(options) params = PostArgParser.new(args, options, config) params.validate! post = PostFileInfo.new(params) file_creator = Compose::FileCreator.new(post, params.force?, params.source) file_creator.create! Compose::FileEditor.bootstrap(config) Compose::FileEditor.open_editor(file_creator.file_path) end class PostArgParser < Compose::ArgParser def date @date ||= options["date"] ? Date.parse(options["date"]) : Time.now end end class PostFileInfo < Compose::FileInfo def resource_type "post" end def path "_posts/#{file_name}" end def file_name "#{_date_stamp}-#{super}" end def _date_stamp @params.date.strftime Jekyll::Compose::DEFAULT_DATESTAMP_FORMAT end def _time_stamp @params.date.strftime @params.timestamp_format end def content(custom_front_matter = {}) default_front_matter = front_matter_defaults_for("posts") custom_front_matter.merge!(default_front_matter) if default_front_matter.is_a?(Hash) super({ "date" => _time_stamp }.merge(custom_front_matter)) end end end end end jekyll-compose-0.12.0/lib/jekyll/commands/unpublish.rb0000644000004100000410000000323213602504365022762 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Commands class Unpublish < Command def self.init_with_program(prog) prog.command(:unpublish) do |c| c.syntax "unpublish POST_PATH" c.description "Moves a post back into the _drafts directory" options.each { |opt| c.option(*opt) } c.action { |args, options| process(args, options) } end end def self.options [ ["config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", Array, "Custom configuration file"], ["force", "-f", "--force", "Overwrite a draft if it already exists"], ] end def self.process(args = [], options = {}) config = configuration_from_options(options) params = UnpublishArgParser.new(args, options, config) params.validate! movement = PostMovementInfo.new(params) mover = PostMover.new(movement, params.force?, params.source) mover.move end end class UnpublishArgParser < Compose::MovementArgParser def resource_type "post" end def name File.basename(path).sub %r!\d{4}-\d{2}-\d{2}-!, "" end end class PostMovementInfo attr_reader :params def initialize(params) @params = params end def from params.path end def to "_drafts/#{params.name}" end def front_matter(data) data.reject { |key, _value| key == "date" } end end class PostMover < Compose::FileMover def resource_type_from "post" end def resource_type_to "draft" end end end end jekyll-compose-0.12.0/lib/jekyll-compose.rb0000644000004100000410000000121213602504365020607 0ustar www-datawww-data# frozen_string_literal: true require "jekyll-compose/version" require "jekyll-compose/arg_parser" require "jekyll-compose/movement_arg_parser" require "jekyll-compose/file_creator" require "jekyll-compose/file_mover" require "jekyll-compose/file_info" require "jekyll-compose/file_editor" module Jekyll module Compose DEFAULT_TYPE = "md" DEFAULT_LAYOUT = "post" DEFAULT_LAYOUT_PAGE = "page" DEFAULT_DATESTAMP_FORMAT = "%Y-%m-%d" DEFAULT_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M %z" end end %w(draft post publish unpublish page rename compose).each do |file| require File.expand_path("jekyll/commands/#{file}.rb", __dir__) end jekyll-compose-0.12.0/lib/jekyll-compose/0000755000004100000410000000000013602504365020266 5ustar www-datawww-datajekyll-compose-0.12.0/lib/jekyll-compose/version.rb0000644000004100000410000000013713602504365022301 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Compose VERSION = "0.12.0" end end jekyll-compose-0.12.0/lib/jekyll-compose/file_creator.rb0000644000004100000410000000205613602504365023254 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Compose class FileCreator attr_reader :file, :force, :root def initialize(file_info, force = false, root = nil) @file = file_info @force = force @root = root end def create! return unless create? ensure_directory_exists write_file end def file_path return file.path if root.nil? || root.empty? File.join(root, file.path) end private def create? return true if force return true unless File.exist?(file_path) Jekyll.logger.warn "A #{file.resource_type} already exists at #{file_path}" false end def ensure_directory_exists dir = File.dirname file_path FileUtils.mkdir_p(dir) unless Dir.exist?(dir) end def write_file File.open(file_path, "w") do |f| f.puts(file.content) end Jekyll.logger.info "New #{file.resource_type} created at #{file_path.cyan}" end end end end jekyll-compose-0.12.0/lib/jekyll-compose/movement_arg_parser.rb0000644000004100000410000000050313602504365024650 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Compose class MovementArgParser < ArgParser def validate! raise ArgumentError, "You must specify a #{resource_type} path." if args.empty? end def path File.join(source, args.join(" ")).sub(%r!\A/!, "") end end end end jekyll-compose-0.12.0/lib/jekyll-compose/file_info.rb0000644000004100000410000000124413602504365022546 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Compose class FileInfo attr_reader :params def initialize(params) @params = params end def file_name name = Jekyll::Utils.slugify params.title "#{name}.#{params.type}" end def content(custom_front_matter = {}) front_matter = YAML.dump({ "layout" => params.layout, "title" => params.title, }.merge(custom_front_matter)) front_matter + "---\n" end private def front_matter_defaults_for(key) params.config.dig("jekyll_compose", "default_front_matter", key) end end end end jekyll-compose-0.12.0/lib/jekyll-compose/arg_parser.rb0000644000004100000410000000175113602504365022744 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Compose class ArgParser attr_reader :args, :options, :config # TODO: Remove `nil` parameter in v1.0 def initialize(args, options, config = nil) @args = args @options = options @config = config || Jekyll.configuration(options) end def validate! raise ArgumentError, "You must specify a name." if args.empty? end def type options["extension"] || Jekyll::Compose::DEFAULT_TYPE end def layout options["layout"] || Jekyll::Compose::DEFAULT_LAYOUT end def title args.join " " end def force? !!options["force"] end def timestamp_format options["timestamp_format"] || Jekyll::Compose::DEFAULT_TIMESTAMP_FORMAT end def source File.join(config["source"], config["collections_dir"]) .gsub(%r!^#{Regexp.quote(Dir.pwd)}/*!, "") end end end end jekyll-compose-0.12.0/lib/jekyll-compose/file_mover.rb0000644000004100000410000000435613602504365022752 0ustar www-datawww-data# frozen_string_literal: true module Jekyll module Compose class FileMover attr_reader :force, :movement, :root def initialize(movement, force = false, root = nil) @movement = movement @force = force @root = root end def resource_type_from "file" end def resource_type_to "file" end def move return unless valid_source? && valid_destination? ensure_directory_exists update_front_matter move_file end def validate_source raise ArgumentError, "There was no #{resource_type_from} found at '#{from}'." unless File.exist? from end def ensure_directory_exists dir = File.dirname to Dir.mkdir(dir) unless Dir.exist?(dir) end def validate_should_write! raise ArgumentError, "A #{resource_type_to} already exists at #{to}" if File.exist?(to) && !force end def move_file FileUtils.mv(from, to) Jekyll.logger.info "#{resource_type_from.capitalize} #{from} was moved to #{to}" end def update_front_matter content = File.read(from) if content =~ Jekyll::Document::YAML_FRONT_MATTER_REGEXP content = $POSTMATCH match = Regexp.last_match[1] if Regexp.last_match data = movement.front_matter(Psych.safe_load(match)) File.write(from, "#{Psych.dump(data)}---\n#{content}") end rescue Psych::SyntaxError => e Jekyll.logger.warn e rescue StandardError => e Jekyll.logger.warn e end private def valid_source? return true if File.exist?(from) invalidate_with "There was no #{resource_type_from} found at '#{from}'." end def valid_destination? return true if force return true unless File.exist?(to) invalidate_with "A #{resource_type_to} already exists at #{to}" end def invalidate_with(msg) Jekyll.logger.warn msg false end def from movement.from end def to file_path(movement.to) end def file_path(path) return path if root.nil? || root.empty? File.join(root, path) end end end end jekyll-compose-0.12.0/lib/jekyll-compose/file_editor.rb0000644000004100000410000000213613602504365023102 0ustar www-datawww-data# frozen_string_literal: true # # This class is aimed to open the created file in the selected editor. # To use this feature specify at Jekyll config: # # ``` # jekyll_compose: # auto_open: true # ``` # # And make sure, that you have JEKYLL_EDITOR, VISUAL, or EDITOR environment variables set up. # This will allow to open the file in your default editor automatically. module Jekyll module Compose class FileEditor class << self attr_reader :compose_config alias_method :jekyll_compose_config, :compose_config def bootstrap(config) @compose_config = config["jekyll_compose"] || {} end def open_editor(filepath) run_editor(post_editor, File.expand_path(filepath)) if post_editor end def run_editor(editor_name, filepath) system("#{editor_name} #{filepath}") end def post_editor return unless auto_open? ENV["JEKYLL_EDITOR"] || ENV["VISUAL"] || ENV["EDITOR"] end def auto_open? compose_config["auto_open"] end end end end end