middleman-blog-drafts 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ # Ignore bundler lock file
2
+ Gemfile.lock
3
+ /tmp
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source :rubygems
2
+
3
+ # Specify your gem's dependencies in middleman-blog-drafts.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem "rake", "~> 0.9.2"
8
+ gem "rdoc", "~> 3.9"
9
+ gem "yard", "~> 0.8.0"
10
+ gem "guard-cucumber"
11
+ end
12
+
13
+ group :test do
14
+ gem "cucumber", "~> 1.2.0"
15
+ gem "fivemat"
16
+ gem "aruba", "~> 0.4.11"
17
+ gem "rspec", "~> 2.7"
18
+ end
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ guard 'cucumber' do
2
+ watch(%r{^features/.+\.feature$})
3
+ watch(%r{^features/support/.+$}) { 'features' }
4
+ watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
5
+ watch(%r{^fixtures/.+$})
6
+ watch(%r{^lib/.+$})
7
+ end
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'cucumber/rake/task'
5
+
6
+ Cucumber::Rake::Task.new(:cucumber, 'Run features that should pass') do |t|
7
+ t.cucumber_opts = "--color --tags ~@wip --strict --format #{ENV['CUCUMBER_FORMAT'] || 'Fivemat'}"
8
+ end
9
+
10
+ require 'rake/clean'
11
+
12
+ task :test => ["cucumber"]
13
+
14
+ task :default => :test
@@ -0,0 +1,5 @@
1
+ Feature: Building the site
2
+ Scenario: Unpublished articles don't get built
3
+ Given a fixture app "draft-date-app"
4
+ When I run `middleman build`
5
+ Then the build directory should be empty
@@ -0,0 +1,7 @@
1
+ Feature: New draft CLI command
2
+ Scenario: Create a new blog draft article with the CLI
3
+ Given a fixture app "drafts-app"
4
+ And I run `middleman draft "My New Article"`
5
+ Then the exit status should be 0
6
+ Then the following files should exist:
7
+ | source/drafts/my-new-article.html.markdown |
@@ -0,0 +1,10 @@
1
+ Feature: Derive draft date from today
2
+ Scenario: Drafts without dates
3
+ Given the Server is running at "draft-date-app"
4
+ When I go to "/drafts/new-draft.html"
5
+ Then I should see the current date
6
+
7
+ Scenario: Drafts without dates and using data store
8
+ Given the Server is running at "draft-date-app"
9
+ When I go to "/drafts/other-draft.html"
10
+ Then I should see the current date
@@ -0,0 +1,17 @@
1
+ Feature: Publish draft CLI command
2
+ Scenario: Publish a new blog draft article with the CLI
3
+ Given a fixture app "drafts-app"
4
+ And I run `middleman draft "My New Article"`
5
+ And I run `middleman publish source/drafts/my-new-article.html.markdown --date 2012-03-07`
6
+ Then the exit status should be 0
7
+ And the following files should exist:
8
+ | source/blog/2012-03-07-my-new-article.html.markdown |
9
+ And the following files should not exist:
10
+ | source/drafts/my-new-article.html.markdown |
11
+
12
+ Scenario: Viewing a published article
13
+ Given a fixture app "draft-date-app"
14
+ And I run `middleman publish source/drafts/new-draft.html.erb --date 2012-03-07`
15
+ When the Server is running at "draft-date-app"
16
+ And I go to "/2012/03/07/new-draft.html"
17
+ Then I should see '2012-03-07'
@@ -0,0 +1,7 @@
1
+ Then /^I should see the current date/ do
2
+ step %Q{I should see "Date: #{Time.now.in_time_zone.iso8601}"}
3
+ end
4
+
5
+ Then /^the build directory should be empty/ do
6
+ in_current_dir { Dir['build/**/*'].should be_empty }
7
+ end
@@ -0,0 +1,4 @@
1
+ PROJECT_ROOT_PATH = File.dirname(File.dirname(File.dirname(__FILE__)))
2
+ require "middleman-core"
3
+ require "middleman-core/step_definitions"
4
+ require File.join(PROJECT_ROOT_PATH, 'lib', 'middleman-blog-drafts')
@@ -0,0 +1,3 @@
1
+ require 'middleman-blog'
2
+ activate :blog
3
+ activate :drafts
@@ -0,0 +1,2 @@
1
+ <h1>New Draft Title</h1>
2
+ <p>Date: <%= current_article.date.iso8601 %></p>
@@ -0,0 +1,2 @@
1
+ <h1>Other Draft Title</h1>
2
+ <p>Date: <%= current_article.data.date.iso8601 %></p>
@@ -0,0 +1,8 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ </head>
5
+ <body>
6
+ <%= yield%>
7
+ </body>
8
+ </html>
@@ -0,0 +1,5 @@
1
+ require 'middleman-blog'
2
+ activate :blog do |c|
3
+ c.prefix = 'blog'
4
+ end
5
+ activate :drafts
@@ -0,0 +1,84 @@
1
+ module Middleman
2
+ module Blog
3
+ module Drafts
4
+ # An extension to let {Middleman::Blog::BlogData} know about all draft
5
+ # articles in the site.
6
+ module BlogDataExtensions
7
+ def drafts(app=nil, options=nil)
8
+ @_drafts ||= Middleman::Blog::Drafts::Data.new(self, app, options)
9
+ end
10
+
11
+ # A draft BlogArticle for the given path, or nil if one doesn't exist.
12
+ # @return [Middleman::Sitemap::Resource]
13
+ def draft(path)
14
+ article = drafts.options.app.sitemap.find_resource_by_path(path.to_s)
15
+ if article && article.is_a?(BlogArticle)
16
+ article
17
+ else
18
+ nil
19
+ end
20
+ end
21
+ end
22
+
23
+ # A store of all the draft articles in the site. Accessed via "blog.drafts" in
24
+ # templates.
25
+ class Data
26
+ attr_reader :options, :path_matcher, :matcher_indexes
27
+
28
+ # @private
29
+ def initialize(blog_data, app, options)
30
+ @blog_data = blog_data
31
+ @options = options
32
+ @app = app
33
+
34
+ # A list of resources corresponding to draft articles
35
+ @_drafts = []
36
+
37
+ matcher = Regexp.escape(options.sources).
38
+ sub(/^\//, "").
39
+ sub(":title", "([^/]+)")
40
+
41
+ @path_matcher = /^#{matcher}/
42
+
43
+ # Build a hash of part name to capture index, e.g. {"year" => 0}
44
+ @matcher_indexes = {}
45
+ options.sources.scan(/:title/).
46
+ each_with_index do |key, i|
47
+ @matcher_indexes[key[1..-1]] = i
48
+ end
49
+ # The path always appears at the end.
50
+ @matcher_indexes["path"] = @matcher_indexes.size
51
+ end
52
+
53
+ # Updates' blog draft articles destination paths to be the
54
+ # permalink.
55
+ # @return [void]
56
+ def manipulate_resource_list(resources)
57
+ @_drafts = []
58
+ used_resources = []
59
+
60
+ resources.each do |resource|
61
+ if resource.path =~ @path_matcher
62
+ resource.extend BlogArticle
63
+ resource.extend DraftArticle
64
+
65
+ next unless @app.environment == :development
66
+
67
+ # compute output path:
68
+ resource.destination_path = options.permalink.
69
+ sub(':title', resource.slug)
70
+
71
+ resource.destination_path = Middleman::Util.normalize_path(resource.destination_path)
72
+
73
+ @_drafts << resource
74
+ end
75
+
76
+ used_resources << resource
77
+ end
78
+
79
+ used_resources
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,43 @@
1
+ require 'middleman-core/cli'
2
+
3
+ module Middleman
4
+ module Cli
5
+ # This class provides a "draft" command for the middleman CLI.
6
+ class Draft < Thor
7
+ include Thor::Actions
8
+
9
+ check_unknown_options!
10
+
11
+ namespace :draft
12
+
13
+ # Template files are relative to this file
14
+ # @return [String]
15
+ def self.source_root
16
+ File.dirname(__FILE__)
17
+ end
18
+
19
+ # Tell Thor to exit with a nonzero exit code on failure
20
+ def self.exit_on_failure?
21
+ true
22
+ end
23
+
24
+ desc "draft TITLE", "Create a new draft for a blog article"
25
+ def draft(title)
26
+ shared_instance = ::Middleman::Application.server.inst
27
+
28
+ # This only exists when the config.rb sets it!
29
+ if shared_instance.blog.respond_to? :drafts
30
+ @title = title
31
+ @slug = title.parameterize
32
+
33
+ draft_path = shared_instance.blog.drafts.options.sources.
34
+ sub(':title', @slug)
35
+
36
+ template "draft.tt", File.join(shared_instance.source_dir, draft_path + shared_instance.blog.options.default_extension)
37
+ else
38
+ raise Thor::Error.new "You need to activate the drafts extension in config.rb before you can create an article"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ ---
2
+ title: '<%= @title %>'
3
+ # date: TBD When publishing
4
+ tags:
5
+ ---
6
+
7
+ # <%= @title %>
8
+
9
+ Write a draft post :-)
@@ -0,0 +1,57 @@
1
+ require 'middleman-core/cli'
2
+
3
+ module Middleman
4
+ module Cli
5
+ # This class provides a "publish" command for the middleman CLI.
6
+ class Publish < Thor
7
+ include Thor::Actions
8
+
9
+ check_unknown_options!
10
+
11
+ namespace :publish
12
+
13
+ # Template files are relative to this file
14
+ # @return [String]
15
+ def self.source_root
16
+ File.dirname(__FILE__)
17
+ end
18
+
19
+ # Tell Thor to exit with a nonzero exit code on failure
20
+ def self.exit_on_failure?
21
+ true
22
+ end
23
+
24
+ desc "publish DRAFT_PATH", "Publish a draft"
25
+ method_option "date",
26
+ :aliases => "-d",
27
+ :desc => "The date to create the post with (defaults to now)"
28
+ def publish(draft_path)
29
+ shared_instance = ::Middleman::Application.server.inst
30
+
31
+ # This only exists when the config.rb sets it!
32
+ if shared_instance.blog.respond_to? :drafts
33
+ @slug = draft_path.split('/').last.split('.').first.parameterize
34
+ @date = options[:date] ? DateTime.parse(options[:date]) : DateTime.now
35
+
36
+ draft_path = File.expand_path draft_path
37
+ extension = File.extname draft_path
38
+
39
+ article_path = shared_instance.blog.options.sources.
40
+ sub(':year', @date.year.to_s).
41
+ sub(':month', @date.month.to_s.rjust(2,'0')).
42
+ sub(':day', @date.day.to_s.rjust(2,'0')).
43
+ sub(':title', @slug)
44
+ article_path = File.join(shared_instance.source_dir, article_path + extension)
45
+
46
+ data, content = shared_instance.frontmatter_manager.data(draft_path)
47
+ data = data.dup.tap { |d| d[:date] = Date.parse @date.strftime('%F') }
48
+
49
+ create_file article_path, "#{YAML::dump(data).sub(/^--- !ruby.*$/, '---')}---\n#{content}"
50
+ remove_file draft_path
51
+ else
52
+ raise Thor::Error.new "You need to activate the drafts extension in config.rb before you can publish an article"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,38 @@
1
+ module Middleman
2
+ module Blog
3
+ module Drafts
4
+ module DraftArticle
5
+ # The "slug" of the draft article that shows up in its URL.
6
+ # @return [String]
7
+ def slug
8
+ @_slug ||= path_part("title")
9
+ end
10
+
11
+ # Retrieve a section of the source path
12
+ # @param [String] The part of the path, e.g. "title"
13
+ # @return [String]
14
+ def path_part(part)
15
+ @_path_parts ||= app.blog.drafts.path_matcher.match(path).captures
16
+
17
+ @_path_parts[app.blog.drafts.matcher_indexes[part]]
18
+ end
19
+
20
+ # Returns current date as we can't guess when the article will be published
21
+ #
22
+ # We need this in place or the layout used for blog posts might blow up
23
+ #
24
+ # @return [TimeWithZone]
25
+ def date
26
+ Time.now.in_time_zone
27
+ end
28
+
29
+ # Extends resource data adding the date field
30
+ #
31
+ # @return [Thor::CoreExt::HashWithIndifferentAccess]
32
+ def data
33
+ super.dup.merge(date: date)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,54 @@
1
+ module Middleman
2
+ module Blog
3
+ module Drafts
4
+ class Options
5
+ attr_accessor :sources, :layout, :permalink
6
+
7
+ def initialize(options={})
8
+ options.each do |k,v|
9
+ self.send(:"#{k}=", v)
10
+ end
11
+ end
12
+ end
13
+
14
+ class << self
15
+ # Called when user `activate`s your extension
16
+ def registered(app, options_hash={}, &block)
17
+ require 'middleman-blog/extension'
18
+ require 'middleman-blog/blog_data'
19
+ require 'middleman-blog-drafts/draft_article'
20
+ require 'middleman-blog-drafts/blog_data_extensions'
21
+
22
+ options = Options.new(options_hash)
23
+ yield options if block_given?
24
+
25
+ options.sources ||= "drafts/:title.html"
26
+ options.permalink ||= "/drafts/:title.html"
27
+
28
+ ::Middleman::Blog::BlogData.send :include, BlogDataExtensions
29
+ app.send :include, Helpers
30
+
31
+ app.after_configuration do
32
+ options.layout = blog.options.layout
33
+ blog.drafts(self, options)
34
+
35
+ sitemap.register_resource_list_manipulator(
36
+ :blog_drafts,
37
+ blog.drafts,
38
+ false
39
+ )
40
+ end
41
+ end
42
+ alias :included :registered
43
+ end
44
+
45
+ module Helpers
46
+ # Get a {Resource} with mixed in {BlogArticle} methods representing the current article.
47
+ # @return [Middleman::Sitemap::Resource]
48
+ def current_article
49
+ super || blog.draft(current_resource.path)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,7 @@
1
+ module Middleman
2
+ module Blog
3
+ module Drafts
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require "middleman-core"
2
+
3
+ require "middleman-blog-drafts/commands/draft"
4
+ require "middleman-blog-drafts/commands/publish"
5
+ require "middleman-blog-drafts/version"
6
+
7
+ ::Middleman::Extensions.register(:drafts) do
8
+ require 'middleman-blog-drafts/extension'
9
+ ::Middleman::Blog::Drafts
10
+ end
@@ -0,0 +1 @@
1
+ require "middleman-blog-drafts"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "middleman-blog-drafts/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "middleman-blog-drafts"
7
+ s.version = Middleman::Blog::Drafts::VERSION
8
+ s.authors = ["Fabio Rehm"]
9
+ s.email = ["fgrehm@gmail.com"]
10
+ s.description = %q{middleman-blog extension for working with draft articles}
11
+ s.summary = s.description
12
+ s.homepage = "https://github.com/fgrehm/middleman-draft-articles"
13
+
14
+ s.files = `git ls-files`.split($/)
15
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_runtime_dependency("middleman-core", [">= 3.0.9"])
20
+ s.add_runtime_dependency("middleman-blog")
21
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: middleman-blog-drafts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Fabio Rehm
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ prerelease: false
16
+ name: middleman-core
17
+ version_requirements: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.9
22
+ none: false
23
+ requirement: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ! '>='
26
+ - !ruby/object:Gem::Version
27
+ version: 3.0.9
28
+ none: false
29
+ type: :runtime
30
+ - !ruby/object:Gem::Dependency
31
+ prerelease: false
32
+ name: middleman-blog
33
+ version_requirements: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ none: false
39
+ requirement: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ none: false
45
+ type: :runtime
46
+ description: middleman-blog extension for working with draft articles
47
+ email:
48
+ - fgrehm@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - Guardfile
56
+ - Rakefile
57
+ - features/build.feature
58
+ - features/draft_cli.feature
59
+ - features/draft_date.feature
60
+ - features/publish_cli.feature
61
+ - features/step_definitions/drafts_steps.rb
62
+ - features/support/env.rb
63
+ - fixtures/draft-date-app/config.rb
64
+ - fixtures/draft-date-app/source/drafts/new-draft.html.erb
65
+ - fixtures/draft-date-app/source/drafts/other-draft.html.erb
66
+ - fixtures/draft-date-app/source/layout.erb
67
+ - fixtures/drafts-app/config.rb
68
+ - lib/middleman-blog-drafts.rb
69
+ - lib/middleman-blog-drafts/blog_data_extensions.rb
70
+ - lib/middleman-blog-drafts/commands/draft.rb
71
+ - lib/middleman-blog-drafts/commands/draft.tt
72
+ - lib/middleman-blog-drafts/commands/publish.rb
73
+ - lib/middleman-blog-drafts/draft_article.rb
74
+ - lib/middleman-blog-drafts/extension.rb
75
+ - lib/middleman-blog-drafts/version.rb
76
+ - lib/middleman_extension.rb
77
+ - middleman-blog-drafts.gemspec
78
+ homepage: https://github.com/fgrehm/middleman-draft-articles
79
+ licenses: []
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ segments:
90
+ - 0
91
+ hash: -3659966068752668471
92
+ none: false
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ segments:
99
+ - 0
100
+ hash: -3659966068752668471
101
+ none: false
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.23
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: middleman-blog extension for working with draft articles
108
+ test_files:
109
+ - features/build.feature
110
+ - features/draft_cli.feature
111
+ - features/draft_date.feature
112
+ - features/publish_cli.feature
113
+ - features/step_definitions/drafts_steps.rb
114
+ - features/support/env.rb
115
+ has_rdoc: