diff --git a/.gitignore b/.gitignore index d87d4be..d651984 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ lib/bundler/man pkg rdoc spec/reports +spec/source test/tmp test/version_tmp tmp diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6268fe5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: ruby +rvm: + - 2.2 + - 2.1 + - 2.0 +script: script/cibuild +sudo: false +cache: bundler diff --git a/Rakefile b/Rakefile index 62b270e..2995527 100644 --- a/Rakefile +++ b/Rakefile @@ -1,9 +1 @@ require "bundler/gem_tasks" -require 'rake/testtask' -Rake::TestTask.new(:test) do |test| - test.libs << 'lib' << 'test' - test.pattern = 'test/**/test_*.rb' - test.verbose = true -end - -task :default => :test diff --git a/jekyll-compose.gemspec b/jekyll-compose.gemspec index 1b80a39..0821006 100644 --- a/jekyll-compose.gemspec +++ b/jekyll-compose.gemspec @@ -1,7 +1,7 @@ # coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'jekyll/compose/version' +require 'jekyll-compose/version' Gem::Specification.new do |spec| spec.name = "jekyll-compose" @@ -20,5 +20,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler", "~> 1.5" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "jekyll", "~> 2.0" - spec.add_development_dependency "shoulda" + spec.add_development_dependency "rspec", "~> 3.0" end diff --git a/lib/jekyll-compose.rb b/lib/jekyll-compose.rb new file mode 100644 index 0000000..92f34ec --- /dev/null +++ b/lib/jekyll-compose.rb @@ -0,0 +1,10 @@ +require "jekyll-compose/version" + +module Jekyll + module Compose + end +end + +%w{draft post publish}.each do |file| + require File.expand_path("jekyll/commands/#{file}.rb", File.dirname(__FILE__)) +end diff --git a/lib/jekyll/compose/version.rb b/lib/jekyll-compose/version.rb similarity index 100% rename from lib/jekyll/compose/version.rb rename to lib/jekyll-compose/version.rb diff --git a/lib/jekyll/commands/draft.rb b/lib/jekyll/commands/draft.rb index e48523f..2bd0be9 100644 --- a/lib/jekyll/commands/draft.rb +++ b/lib/jekyll/commands/draft.rb @@ -16,11 +16,11 @@ def self.init_with_program(prog) end end - def self.process(args, options = {}) + def self.process(args = [], options = {}) raise ArgumentError.new('You must specify a name.') if args.empty? - - type = options["type"].nil? ? "markdown" : options["type"] - layout = options["layout"].nil? ? "post" : options["layout"] + + type = options["type"] || "markdown" + layout = options["layout"] || "post" title = args.shift name = title.gsub(' ', '-').downcase @@ -34,7 +34,7 @@ def self.process(args, options = {}) end puts "New draft created at ./#{draft_path}.\n" - end + end # Internal: Gets the filename of the draft to be created # # Returns the filename of the draft, as a String @@ -43,10 +43,10 @@ def self.draft_name(name, ext='markdown') end def self.front_matter(layout, title) - "--- -layout: #{layout} -title: #{title} ----" + { + "layout" => layout, + "title" => title, + }.to_yaml + "\n---\n" end end end diff --git a/lib/jekyll/commands/post.rb b/lib/jekyll/commands/post.rb index 185895d..da35d02 100644 --- a/lib/jekyll/commands/post.rb +++ b/lib/jekyll/commands/post.rb @@ -17,9 +17,9 @@ def self.init_with_program(prog) end end - def self.process(args, options = {}) + def self.process(args = [], options = {}) raise ArgumentError.new('You must specify a name.') if args.empty? - + type = options["type"].nil? ? "markdown" : options["type"] layout = options["layout"].nil? ? "post" : options["layout"] @@ -37,7 +37,7 @@ def self.process(args, options = {}) end puts "New post created at ./#{post_path}.\n" - end + end # Internal: Gets the filename of the draft to be created # # Returns the filename of the draft, as a String diff --git a/lib/jekyll/commands/publish.rb b/lib/jekyll/commands/publish.rb index 3f7355e..83264f8 100644 --- a/lib/jekyll/commands/publish.rb +++ b/lib/jekyll/commands/publish.rb @@ -14,7 +14,7 @@ def self.init_with_program(prog) end end - def self.process(args, options = {}) + def self.process(args = [], options = {}) raise ArgumentError.new('You must specify a draft path.') if args.empty? date = options["date"].nil? ? Date.today : Date.parse(options["date"]) diff --git a/lib/jekyll/compose.rb b/lib/jekyll/compose.rb deleted file mode 100644 index 38b80b5..0000000 --- a/lib/jekyll/compose.rb +++ /dev/null @@ -1,10 +0,0 @@ -require "jekyll/compose/version" - -module Jekyll - module Compose - end -end - -require "jekyll/commands/draft" -require "jekyll/commands/post" -require "jekyll/commands/publish" diff --git a/script/bootstrap b/script/bootstrap new file mode 100755 index 0000000..b8e2d4a --- /dev/null +++ b/script/bootstrap @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +bundle install --jobs=8 --retry=3 diff --git a/script/cibuild b/script/cibuild new file mode 100755 index 0000000..e1d528f --- /dev/null +++ b/script/cibuild @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +script/test --format progress $@ diff --git a/script/test b/script/test new file mode 100755 index 0000000..365880c --- /dev/null +++ b/script/test @@ -0,0 +1,2 @@ +#!/bin/bash +bundle exec rspec --color --require spec_helper $@ diff --git a/spec/draft_spec.rb b/spec/draft_spec.rb new file mode 100644 index 0000000..0d03d73 --- /dev/null +++ b/spec/draft_spec.rb @@ -0,0 +1,58 @@ +RSpec.describe(Jekyll::Commands::Draft) do + let(:name) { 'A test post' } + let(:args) { [name] } + let(:drafts_dir) { Pathname.new source_dir('_drafts') } + let(:path) { drafts_dir.join('a-test-post.markdown') } + + before(:all) do + FileUtils.mkdir_p source_dir unless File.directory? source_dir + Dir.chdir source_dir + end + + before(:each) do + FileUtils.mkdir_p drafts_dir unless File.directory? drafts_dir + end + + after(:each) do + FileUtils.rm_r drafts_dir if File.directory? drafts_dir + end + + it 'creates a new draft' do + expect(path).not_to exist + capture_stdout { described_class.process(args) } + expect(path).to exist + end + + it 'writes a helpful success message' do + output = capture_stdout { described_class.process(args) } + expect(output).to eql("New draft created at ./_drafts/a-test-post.markdown.\n") + end + + it 'errors with no arguments' do + expect(-> { + capture_stdout { described_class.process } + }).to raise_error('You must specify a name.') + end + + context 'when the draft already exists' do + let(:name) { 'An existing draft' } + let(:path) { drafts_dir.join('an-existing-draft.markdown') } + + before(:each) do + FileUtils.touch path + end + + it 'raises an error' do + expect(-> { + capture_stdout { described_class.process(args) } + }).to raise_error("A draft already exists at ./_drafts/an-existing-draft.markdown") + end + + it 'overwrites if --force is given' do + expect(-> { + capture_stdout { described_class.process(args, 'force' => true) } + }).not_to raise_error + expect(File.read(path)).to match(/layout: post/) + end + end +end diff --git a/spec/post_spec.rb b/spec/post_spec.rb new file mode 100644 index 0000000..e9b27a5 --- /dev/null +++ b/spec/post_spec.rb @@ -0,0 +1,59 @@ +RSpec.describe(Jekyll::Commands::Post) do + let(:name) { 'A test post' } + let(:args) { [name] } + let(:posts_dir) { Pathname.new source_dir('_posts') } + let(:filename) { "#{Time.now.strftime('%Y-%m-%d')}-a-test-post.markdown" } + let(:path) { posts_dir.join(filename) } + + before(:all) do + FileUtils.mkdir_p source_dir unless File.directory? source_dir + Dir.chdir source_dir + end + + before(:each) do + FileUtils.mkdir_p posts_dir unless File.directory? posts_dir + end + + after(:each) do + FileUtils.rm_r posts_dir if File.directory? posts_dir + end + + it 'creates a new post' do + expect(path).not_to exist + capture_stdout { described_class.process(args) } + expect(path).to exist + end + + it 'should write a helpful message when successful' do + output = capture_stdout { described_class.process(args) } + expect(output).to eql("New post created at ./_posts/#{filename}.\n") + end + + it 'errors with no arguments' do + expect(-> { + capture_stdout { described_class.process } + }).to raise_error('You must specify a name.') + end + + context 'when the post already exists' do + let(:name) { 'An existing post' } + let(:filename) { "#{Time.now.strftime('%Y-%m-%d')}-an-existing-post.markdown" } + + before(:each) do + FileUtils.touch path + end + + it 'raises an error' do + expect(-> { + capture_stdout { described_class.process(args) } + }).to raise_error("A post already exists at ./_posts/#{filename}") + end + + it 'overwrites if --force is given' do + expect(-> { + capture_stdout { described_class.process(args, 'force' => true) } + }).not_to raise_error + expect(File.read(path)).to match(/layout: post/) + end + end +end diff --git a/spec/publish_spec.rb b/spec/publish_spec.rb new file mode 100644 index 0000000..12725d7 --- /dev/null +++ b/spec/publish_spec.rb @@ -0,0 +1,55 @@ +RSpec.describe(Jekyll::Commands::Publish) do + let(:drafts_dir) { source_dir('_drafts') } + let(:posts_dir) { source_dir('_posts') } + let(:draft_to_publish) { 'a-test-post.markdown' } + let(:post_filename) { "#{Time.now.strftime('%Y-%m-%d')}-#{draft_to_publish}" } + let(:args) { ["_drafts/#{draft_to_publish}"] } + + let(:draft_path) { Pathname.new(File.join(drafts_dir, draft_to_publish)) } + let(:post_path) { Pathname.new(File.join(posts_dir, post_filename))} + + before(:all) do + FileUtils.mkdir_p source_dir unless File.directory? source_dir + Dir.chdir source_dir + end + + before(:each) do + FileUtils.mkdir_p drafts_dir unless File.directory? drafts_dir + FileUtils.mkdir_p posts_dir unless File.directory? posts_dir + FileUtils.touch draft_path + end + + after(:each) do + FileUtils.rm_r drafts_dir if File.directory? drafts_dir + FileUtils.rm_r posts_dir if File.directory? posts_dir + FileUtils.rm_r draft_path if File.file? draft_path + FileUtils.rm_r post_path if File.file? post_path + end + + it 'publishes a draft post' do + expect(Pathname.new(post_path)).not_to exist + expect(Pathname.new(draft_path)).to exist + capture_stdout { described_class.process(args) } + expect(Pathname.new(post_path)).to exist + end + + it 'writes a helpful message on success' do + expect(Pathname.new(draft_path)).to exist + output = capture_stdout { described_class.process(args) } + expect(output).to eql("Draft _drafts/#{draft_to_publish} was published to ./_posts/#{post_filename}\n") + end + + it 'errors if there is no argument' do + expect(-> { + capture_stdout { described_class.process } + }).to raise_error('You must specify a draft path.') + end + + it 'errors if no file exists at given path' do + weird_path = '_drafts/i-do-not-exist.markdown' + expect(-> { + capture_stdout { described_class.process [weird_path] } + }).to raise_error("There was no draft found at '_drafts/i-do-not-exist.markdown'.") + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..6f2b361 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,57 @@ +require 'jekyll' +require File.expand_path('../../lib/jekyll-compose.rb', __FILE__) + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.filter_run :focus + config.run_all_when_everything_filtered = true + + config.disable_monkey_patching! + + config.warnings = true + + if config.files_to_run.one? + config.default_formatter = 'doc' + end + + config.profile_examples = 3 + + config.order = :random + + Kernel.srand config.seed + + ### + ### Helper methods + ### + TEST_DIR = File.expand_path('../', __FILE__) + def test_dir(*files) + File.expand_path(File.join(TEST_DIR, *files)) + end + + def source_dir(*files) + test_dir('source', *files) + end + + def fixture_site + Jekyll::Site.new(Jekyll::Utils.deep_merge_hashes( + Jekyll::Configuration::DEFAULTS, + { 'source' => source_dir, 'destination' => test_dir('dest') } + )) + end + + def capture_stdout + $old_stdout = $stdout + $stdout = StringIO.new + yield + $stdout.rewind + return $stdout.string + ensure + $stdout = $old_stdout + end +end diff --git a/test/test_draft_command.rb b/test/test_draft_command.rb deleted file mode 100644 index 47b9c0a..0000000 --- a/test/test_draft_command.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'helper' -require 'jekyll/commands/draft' - -class TestDraftCommand < Test::Unit::TestCase - - context 'when no flags are given' do - setup do - @name = 'A test post' - @args = [@name] - @drafts_dir = '_drafts' - @path = File.join(@drafts_dir, 'a-test-post.markdown') - stub(Jekyll).configuration { Jekyll::Configuration::DEFAULTS } - @site = Site.new(Jekyll.configuration) - FileUtils.mkdir @drafts_dir - end - - teardown do - FileUtils.rm_r @drafts_dir - end - - should 'create a new draft post' do - assert !File.exist?(@path) - capture_stdout { Jekyll::Commands::Draft.process(@args) } - assert File.exist?(@path) - end - - should 'write a success message' do - output = capture_stdout { Jekyll::Commands::Draft.process(@args) } - success_message = "New draft created at ./#{@path}.\n" - assert_equal success_message, output - end - end - - context 'when the draft already exists' do - setup do - @name = 'An existing draft' - @args = [@name] - @drafts_dir = '_drafts' - @path = File.join(@drafts_dir, 'an-existing-draft.markdown') - stub(Jekyll).configuration { Jekyll::Configuration::DEFAULTS } - @site = Site.new(Jekyll.configuration) - FileUtils.mkdir @drafts_dir - FileUtils.touch @path - end - - teardown do - FileUtils.rm_r @drafts_dir - end - - should 'raise an ArgumentError' do - exception = assert_raise ArgumentError do - capture_stdout { Jekyll::Commands::Draft.process(@args) } - end - assert_equal "A draft already exists at ./#{@path}", exception.message - end - - should 'overwrite the existing draft if --force is given' do - capture_stdout { Jekyll::Commands::Draft.process(@args, { "force" => true }) } - assert File.readlines(@path).grep(/layout: post/).any? - end - - end - - context 'when no args are given' do - setup do - @empty_args = [] - end - - should 'raise an ArgumentError' do - exception = assert_raise ArgumentError do - Jekyll::Commands::Draft.process(@empty_args) - end - assert_equal 'You must specify a name.', exception.message - end - end - -end diff --git a/test/test_post_command.rb b/test/test_post_command.rb deleted file mode 100644 index 70c6580..0000000 --- a/test/test_post_command.rb +++ /dev/null @@ -1,73 +0,0 @@ -require 'helper' -require 'jekyll/commands/post' - -class TestPostCommand < Test::Unit::TestCase - - context 'when no flags are given' do - setup do - @name = 'A test post' - @args = [@name] - @posts_dir = '_posts' - @path = File.join(@posts_dir, "#{Time.now.strftime('%Y-%m-%d')}-a-test-post.markdown") - FileUtils.mkdir @posts_dir - end - - teardown do - FileUtils.rm_r @posts_dir - end - - should 'create a new post' do - assert !File.exist?(@path) - capture_stdout { Jekyll::Commands::Post.process(@args) } - assert File.exist?(@path) - end - - should 'write a success message' do - output = capture_stdout { Jekyll::Commands::Post.process(@args) } - success_message = "New post created at ./#{@path}.\n" - assert_equal success_message, output - end - end - - context 'when the post already exists' do - setup do - @name = 'An existing post' - @args = [@name] - @posts_dir = '_posts' - @path = File.join(@posts_dir, "#{Time.now.strftime('%Y-%m-%d')}-an-existing-post.markdown") - FileUtils.mkdir @posts_dir - FileUtils.touch @path - end - - teardown do - FileUtils.rm_r @posts_dir - end - - should 'raise an ArgumentError' do - exception = assert_raise ArgumentError do - capture_stdout { Jekyll::Commands::Post.process(@args) } - end - assert_equal "A post already exists at ./#{@path}", exception.message - end - - should 'overwrite the existing post if --force is given' do - capture_stdout { Jekyll::Commands::Post.process(@args, { "force" => true }) } - assert File.readlines(@path).grep(/layout: post/).any? - end - - end - - context 'when no args are given' do - setup do - @empty_args = [] - end - - should 'raise an ArgumentError' do - exception = assert_raise ArgumentError do - Jekyll::Commands::Post.process(@empty_args) - end - assert_equal 'You must specify a name.', exception.message - end - end - -end diff --git a/test/test_publish.rb b/test/test_publish.rb deleted file mode 100644 index 3603663..0000000 --- a/test/test_publish.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'helper' -require 'date' -require 'jekyll/commands/publish' - -class TestPublishCommand < Test::Unit::TestCase - - context 'when no flags are given' do - setup do - @name = 'a-test-post.markdown' - @args = [@name] - @drafts_dir = '_drafts' - @posts_dir = '_posts' - @draft_path = File.join(@drafts_dir, 'a-test-post.markdown') - @post_path = File.join(@posts_dir, "#{Time.now.strftime('%Y-%m-%d')}-#{@name}") - FileUtils.mkdir @drafts_dir - FileUtils.mkdir @posts_dir - FileUtils.touch @draft_path - end - - teardown do - FileUtils.rm_r @drafts_dir - FileUtils.rm_r @posts_dir - end - - should 'publish a draft post' do - assert !File.exist?(@post_path), 'Post already exists' - assert File.exist?(@draft_path), 'Draft does not exist' - capture_stdout { Jekyll::Commands::Publish.process(@args) } - assert File.exist?(@post_path), 'Post does not exist' - end - - should 'write a success message' do - output = capture_stdout { Jekyll::Commands::Publish.process(@args) } - success_message = "Draft #{@name} was published to ./#{@post_path}\n" - assert_equal success_message, output - end - end - - context 'when no args are given' do - setup do - @empty_args = [] - end - - should 'raise an ArgumentError' do - exception = assert_raise ArgumentError do - Jekyll::Commands::Publish.process(@empty_args) - end - assert_equal 'You must specify a name.', exception.message - end - end - -end