middleman-blog 0.1.5 → 3.0.0.beta.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +5 -0
  3. data/.travis.yml +9 -0
  4. data/.yardopts +3 -0
  5. data/Gemfile +19 -0
  6. data/LICENSE +20 -0
  7. data/README.md +62 -0
  8. data/Rakefile +15 -0
  9. data/features/article_cli.feature +7 -0
  10. data/features/blog_sources.feature +9 -0
  11. data/features/calendar.feature +30 -0
  12. data/features/directory_indexes.feature +6 -0
  13. data/features/encoding.feature +0 -0
  14. data/features/filename_date.feature +9 -0
  15. data/features/next_previous.feature +8 -0
  16. data/features/preview.feature +29 -0
  17. data/features/support/env.rb +4 -0
  18. data/features/tags.feature +50 -0
  19. data/fixtures/blog-sources-app/config.rb +3 -0
  20. data/fixtures/blog-sources-app/source/_article_template.erb +1 -0
  21. data/fixtures/blog-sources-app/source/blog/2011-01-01-new-article.html.markdown +6 -0
  22. data/fixtures/blog-sources-app/source/index.html.erb +9 -0
  23. data/fixtures/blog-sources-app/source/layout.erb +13 -0
  24. data/fixtures/calendar-app/config.rb +5 -0
  25. data/fixtures/calendar-app/source/blog/2011-01-01-new-article.html.markdown +7 -0
  26. data/fixtures/calendar-app/source/blog/2011-01-02-another-article.html.markdown +8 -0
  27. data/fixtures/calendar-app/source/calendar.html.erb +13 -0
  28. data/fixtures/calendar-app/source/index.html.erb +3 -0
  29. data/fixtures/calendar-app/source/layout.erb +15 -0
  30. data/fixtures/filename-date-app/config.rb +1 -0
  31. data/fixtures/filename-date-app/source/2011-01-01-new-article.html.markdown +5 -0
  32. data/fixtures/filename-date-app/source/2011-01-03-filename-and-frontmatter.html.markdown +6 -0
  33. data/fixtures/filename-date-app/source/layout.erb +14 -0
  34. data/fixtures/indexes-app/config.rb +5 -0
  35. data/fixtures/indexes-app/source/2011/01/01/new-article.html.markdown +6 -0
  36. data/fixtures/indexes-app/source/_article_template.erb +1 -0
  37. data/fixtures/indexes-app/source/index.html.erb +9 -0
  38. data/fixtures/indexes-app/source/layout.erb +13 -0
  39. data/fixtures/preview-app/config.rb +3 -0
  40. data/fixtures/preview-app/source/2011/01/01/new-article.html.markdown +6 -0
  41. data/fixtures/preview-app/source/_article_template.erb +10 -0
  42. data/fixtures/preview-app/source/index.html.erb +9 -0
  43. data/fixtures/preview-app/source/layout.erb +22 -0
  44. data/fixtures/tags-app/config.rb +6 -0
  45. data/fixtures/tags-app/source/blog/2011-01-01-new-article.html.markdown +7 -0
  46. data/fixtures/tags-app/source/blog/2011-01-02-another-article.html.markdown +8 -0
  47. data/fixtures/tags-app/source/index.html.erb +3 -0
  48. data/fixtures/tags-app/source/layout.erb +13 -0
  49. data/fixtures/tags-app/source/tag.html.erb +7 -0
  50. data/fixtures/utf8ouch/config.rb +17 -0
  51. data/fixtures/utf8ouch/source/2010/11/13/telling-time.html.markdown +73 -0
  52. data/fixtures/utf8ouch/source/2011/01/01/new-article.html.markdown +6 -0
  53. data/{lib/middleman-blog/template → fixtures/utf8ouch}/source/_article_template.erb +0 -0
  54. data/{lib/middleman-blog/template → fixtures/utf8ouch}/source/archives/index.html.erb +0 -0
  55. data/fixtures/utf8ouch/source/feed.xml.builder +23 -0
  56. data/fixtures/utf8ouch/source/index.html.erb +9 -0
  57. data/fixtures/utf8ouch/source/layout.erb +30 -0
  58. data/lib/middleman-blog.rb +10 -2
  59. data/lib/middleman-blog/blog_article.rb +123 -0
  60. data/lib/middleman-blog/blog_data.rb +94 -0
  61. data/lib/middleman-blog/calendar_pages.rb +85 -0
  62. data/lib/middleman-blog/commands/article.rb +52 -0
  63. data/lib/middleman-blog/commands/article.tt +6 -0
  64. data/lib/middleman-blog/extension.rb +170 -0
  65. data/lib/middleman-blog/tag_pages.rb +33 -0
  66. data/lib/middleman-blog/template.rb +19 -8
  67. data/lib/middleman-blog/template/config.tt +101 -5
  68. data/lib/middleman-blog/template/shared/Gemfile.tt +4 -0
  69. data/lib/middleman-blog/template/source/2012-01-01-example-article.html.markdown +7 -0
  70. data/lib/middleman-blog/template/source/calendar.html.erb +15 -0
  71. data/lib/middleman-blog/template/source/feed.xml.builder +2 -2
  72. data/lib/middleman-blog/template/source/index.html.erb +8 -9
  73. data/lib/middleman-blog/template/source/layout.erb +18 -12
  74. data/lib/middleman-blog/template/source/tag.html.erb +7 -0
  75. data/lib/middleman-blog/version.rb +1 -1
  76. data/lib/{middleman_init.rb → middleman_extension.rb} +0 -0
  77. data/middleman-blog.gemspec +1 -2
  78. metadata +110 -63
  79. data/lib/middleman-blog/feature.rb +0 -162
  80. data/lib/middleman-blog/template/config.ru +0 -9
  81. data/lib/middleman-blog/template/source/2011/01/01/new-article.html.markdown +0 -6
@@ -0,0 +1,94 @@
1
+ module Middleman
2
+ module Blog
3
+ # A store of all the blog articles in the site, with accessors
4
+ # for the articles by various dimensions. Accessed via "blog" in
5
+ # templates.
6
+ class BlogData
7
+ # A regex for matching blog article source paths
8
+ # @return [Regex]
9
+ attr_reader :path_matcher
10
+
11
+ # The configured options for this blog
12
+ # @return [Thor::CoreExt::HashWithIndifferentAccess]
13
+ attr_reader :options
14
+
15
+ # @private
16
+ def initialize(app, options={})
17
+ @app = app
18
+ @options = options
19
+
20
+ # A list of resources corresponding to blog articles
21
+ @_articles = []
22
+
23
+ matcher = Regexp.escape(options.sources).
24
+ sub(/^\//, "").
25
+ sub(":year", "(\\d{4})").
26
+ sub(":month", "(\\d{2})").
27
+ sub(":day", "(\\d{2})").
28
+ sub(":title", "(.*)")
29
+
30
+ @path_matcher = /^#{matcher}/
31
+ end
32
+
33
+ # A list of all blog articles, sorted by date
34
+ # @return [Array<Middleman::Sitemap::Resource>]
35
+ def articles
36
+ @_articles.sort do |a,b|
37
+ b.date <=> a.date
38
+ end
39
+ end
40
+
41
+ # The BlogArticle for the given path, or nil if one doesn't exist.
42
+ # @return [Middleman::Sitemap::Resource]
43
+ def article(path)
44
+ article = @app.sitemap.find_resource_by_path(path.to_s)
45
+ if article && article.is_a?(BlogArticle)
46
+ article
47
+ else
48
+ nil
49
+ end
50
+ end
51
+
52
+ # Returns a map from tag name to an array
53
+ # of BlogArticles associated with that tag.
54
+ # @return [Hash<String, Array<Middleman::Sitemap::Resource>>]
55
+ def tags
56
+ tags = {}
57
+ @_articles.each do |article|
58
+ article.tags.each do |tag|
59
+ tags[tag] ||= []
60
+ tags[tag] << article
61
+ end
62
+ end
63
+
64
+ tags
65
+ end
66
+
67
+ # Updates' blog articles destination paths to be the
68
+ # permalink.
69
+ # @return [void]
70
+ def manipulate_resource_list(resources)
71
+ @_articles = []
72
+
73
+ resources.each do |resource|
74
+ if resource.path =~ path_matcher
75
+ resource.extend BlogArticle
76
+ resource.slug = $4
77
+
78
+ # compute output path:
79
+ # substitute date parts to path pattern
80
+ resource.destination_path = options.permalink.
81
+ sub(':year', resource.date.year.to_s).
82
+ sub(':month', resource.date.month.to_s.rjust(2,'0')).
83
+ sub(':day', resource.date.day.to_s.rjust(2,'0')).
84
+ sub(':title', resource.slug)
85
+
86
+ resource.destination_path = Middleman::Util.normalize_path(resource.destination_path)
87
+
88
+ @_articles << resource
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,85 @@
1
+ module Middleman
2
+ module Blog
3
+
4
+ # A sitemap plugin that adds month/day/year pages to the sitemap
5
+ # based on the dates of blog articles.
6
+ class CalendarPages
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ # Update the main sitemap resource list
12
+ # @return [void]
13
+ def manipulate_resource_list(resources)
14
+ new_resources = []
15
+ # Set up date pages if the appropriate templates have been specified
16
+ @app.blog.articles.group_by {|a| a.date.year }.each do |year, year_articles|
17
+ if @app.blog.options.year_template
18
+ @app.ignore @app.blog.options.year_template
19
+
20
+ path = Middleman::Util.normalize_path(@app.blog_year_path(year))
21
+
22
+ p = ::Middleman::Sitemap::Resource.new(
23
+ @app.sitemap,
24
+ path
25
+ )
26
+ p.proxy_to(@app.blog.options.year_template)
27
+
28
+ p.add_metadata do
29
+ @year = year
30
+ @articles = year_articles
31
+ end
32
+
33
+ new_resources << p
34
+ end
35
+
36
+ year_articles.group_by {|a| a.date.month }.each do |month, month_articles|
37
+ if @app.blog.options.month_template
38
+ @app.ignore @app.blog.options.month_template
39
+
40
+ path = Middleman::Util.normalize_path(@app.blog_month_path(year, month))
41
+
42
+ p = ::Middleman::Sitemap::Resource.new(
43
+ @app.sitemap,
44
+ path
45
+ )
46
+ p.proxy_to(@app.blog.options.month_template)
47
+
48
+ p.add_metadata do
49
+ @year = year
50
+ @month = month
51
+ @articles = month_articles
52
+ end
53
+
54
+ new_resources << p
55
+ end
56
+
57
+ month_articles.group_by {|a| a.date.day }.each do |day, day_articles|
58
+ if @app.blog.options.day_template
59
+ @app.ignore @app.blog.options.day_template
60
+
61
+ path = Middleman::Util.normalize_path(@app.blog_day_path(year, month, day))
62
+ p = ::Middleman::Sitemap::Resource.new(
63
+ @app.sitemap,
64
+ path
65
+ )
66
+ p.proxy_to(@app.blog.options.month_template)
67
+
68
+ p.add_metadata do
69
+ @year = year
70
+ @month = month
71
+ @day = day
72
+ @articles = day_articles
73
+ end
74
+
75
+ new_resources << p
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ resources + new_resources
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,52 @@
1
+ require 'middleman-core/cli'
2
+ require 'date'
3
+
4
+ module Middleman
5
+ module Cli
6
+ # This class provides an "article" command for the middleman CLI.
7
+ class Article < Thor
8
+ include Thor::Actions
9
+
10
+ check_unknown_options!
11
+
12
+ namespace :article
13
+
14
+ # Template files are relative to this file
15
+ # @return [String]
16
+ def self.source_root
17
+ File.dirname(__FILE__)
18
+ end
19
+
20
+ # Tell Thor to exit with a nonzero exit code on failure
21
+ def self.exit_on_failure?
22
+ true
23
+ end
24
+
25
+ desc "article TITLE", "Create a new blog article"
26
+ method_option "date",
27
+ :aliases => "-d",
28
+ :desc => "The date to create the post with (defaults to now)"
29
+ def article(title)
30
+ shared_instance = ::Middleman::Application.server.inst
31
+
32
+ # This only exists when the config.rb sets it!
33
+ if shared_instance.respond_to? :blog
34
+ @title = title
35
+ @slug = title.parameterize
36
+ @date = options[:date] ? DateTime.parse(options[:date]) : DateTime.now
37
+
38
+ article_path = shared_instance.blog.options.sources.
39
+ sub(':year', @date.year.to_s).
40
+ sub(':month', @date.month.to_s.rjust(2,'0')).
41
+ sub(':day', @date.day.to_s.rjust(2,'0')).
42
+ sub(':title', @slug)
43
+
44
+ template "article.tt", File.join(shared_instance.source_dir, article_path + shared_instance.blog.options.default_extension)
45
+ else
46
+ raise Thor::Error.new "You need to activate the blog extension in config.rb before you can create an article"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: <%= @title %>
3
+ date: <%= @date.strftime('%F %R %Z') %>
4
+ tags:
5
+ ---
6
+
@@ -0,0 +1,170 @@
1
+ module Middleman
2
+ module Blog
3
+ class Options
4
+ KEYS = [
5
+ :prefix,
6
+ :permalink,
7
+ :sources,
8
+ :taglink,
9
+ :layout,
10
+ :summary_separator,
11
+ :summary_length,
12
+ :year_link,
13
+ :month_link,
14
+ :day_link,
15
+ :default_extension,
16
+ :calendar_template,
17
+ :year_template,
18
+ :month_template,
19
+ :day_template,
20
+ :tag_template
21
+ ]
22
+
23
+ KEYS.each do |name|
24
+ attr_accessor name
25
+ end
26
+
27
+ def initialize(options={})
28
+ options.each do |k,v|
29
+ self.send(:"#{k}=", v)
30
+ end
31
+ end
32
+ end
33
+
34
+ class << self
35
+ def registered(app, options_hash={}, &block)
36
+ require 'middleman-blog/blog_data'
37
+ require 'middleman-blog/blog_article'
38
+
39
+ app.send :include, Helpers
40
+
41
+ options = Options.new(options_hash)
42
+ yield options if block_given?
43
+
44
+ options.permalink ||= "/:year/:month/:day/:title.html"
45
+ options.sources ||= ":year-:month-:day-:title.html"
46
+ options.taglink ||= "tags/:tag.html"
47
+ options.layout ||= "layout"
48
+ options.summary_separator ||= /(READMORE)/
49
+ options.summary_length ||= 250
50
+ options.year_link ||= "/:year.html"
51
+ options.month_link ||= "/:year/:month.html"
52
+ options.day_link ||= "/:year/:month/:day.html"
53
+ options.default_extension ||= ".markdown"
54
+
55
+ # optional: :tag_template
56
+ # optional: :year_template
57
+ # optional: :month_template
58
+ # optional: :day_template
59
+ # Allow one setting to set all the calendar templates
60
+ if options.calendar_template
61
+ options.year_template ||= options.calendar_template
62
+ options.month_template ||= options.calendar_template
63
+ options.day_template ||= options.calendar_template
64
+ end
65
+
66
+ # If "prefix" option is specified, all other paths are relative to it.
67
+ if options.prefix
68
+ options.prefix = "/#{options.prefix}" unless options.prefix.start_with? '/'
69
+ options.permalink = File.join(options.prefix, options.permalink)
70
+ options.sources = File.join(options.prefix, options.sources)
71
+ options.taglink = File.join(options.prefix, options.taglink)
72
+ options.year_link = File.join(options.prefix, options.year_link)
73
+ options.month_link = File.join(options.prefix, options.month_link)
74
+ options.day_link = File.join(options.prefix, options.day_link)
75
+ end
76
+
77
+ app.after_configuration do
78
+ # Initialize blog with options
79
+ blog(options)
80
+
81
+ sitemap.register_resource_list_manipulator(
82
+ :blog_articles,
83
+ blog,
84
+ false
85
+ )
86
+
87
+ if options.tag_template
88
+ ignore options.tag_template
89
+
90
+ require 'middleman-blog/tag_pages'
91
+ sitemap.register_resource_list_manipulator(
92
+ :blog_tags,
93
+ TagPages.new(self),
94
+ false
95
+ )
96
+ end
97
+
98
+ if options.year_template ||
99
+ options.month_template ||
100
+ options.day_template
101
+
102
+ require 'middleman-blog/calendar_pages'
103
+ sitemap.register_resource_list_manipulator(
104
+ :blog_calendar,
105
+ CalendarPages.new(self),
106
+ false
107
+ )
108
+ end
109
+ end
110
+ end
111
+ alias :included :registered
112
+ end
113
+
114
+ # Helpers for use within templates and layouts.
115
+ module Helpers
116
+ # Get the {BlogData} for this site.
117
+ # @return [BlogData]
118
+ def blog(options=nil)
119
+ @_blog ||= BlogData.new(self, options)
120
+ end
121
+
122
+ # Determine whether the currently rendering template is a blog article.
123
+ # This can be useful in layouts.
124
+ # @return [Boolean]
125
+ def is_blog_article?
126
+ !current_article.nil?
127
+ end
128
+
129
+ # Get a {Resource} with mixed in {BlogArticle} methods representing the current article.
130
+ # @return [Middleman::Sitemap::Resource]
131
+ def current_article
132
+ blog.article(current_page.path)
133
+ end
134
+
135
+ # Get a path to the given tag, based on the :taglink setting.
136
+ # @param [String] tag
137
+ # @return [String]
138
+ def tag_path(tag)
139
+ blog.options.taglink.sub(':tag', tag.parameterize)
140
+ end
141
+
142
+ # Get a path to the given year-based calendar page, based on the :year_link setting.
143
+ # @param [Number] year
144
+ # @return [String]
145
+ def blog_year_path(year)
146
+ blog.options.year_link.sub(':year', year.to_s)
147
+ end
148
+
149
+ # Get a path to the given month-based calendar page, based on the :month_link setting.
150
+ # @param [Number] year
151
+ # @param [Number] month
152
+ # @return [String]
153
+ def blog_month_path(year, month)
154
+ blog.options.month_link.sub(':year', year.to_s).
155
+ sub(':month', month.to_s.rjust(2,'0'))
156
+ end
157
+
158
+ # Get a path to the given day-based calendar page, based on the :day_link setting.
159
+ # @param [Number] year
160
+ # @param [Number] month
161
+ # @param [Number] day
162
+ # @return [String]
163
+ def blog_day_path(year, month, day)
164
+ blog.options.day_link.sub(':year', year.to_s).
165
+ sub(':month', month.to_s.rjust(2,'0')).
166
+ sub(':day', day.to_s.rjust(2,'0'))
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,33 @@
1
+ module Middleman
2
+ module Blog
3
+
4
+ # A sitemap plugin that adds tag pages to the sitemap
5
+ # based on the tags of blog articles.
6
+ class TagPages
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ # Update the main sitemap resource list
12
+ # @return [void]
13
+ def manipulate_resource_list(resources)
14
+ resources + @app.blog.tags.map do |tag, articles|
15
+ path = Middleman::Util.normalize_path(@app.tag_path(tag))
16
+
17
+ p = ::Middleman::Sitemap::Resource.new(
18
+ @app.sitemap,
19
+ path
20
+ )
21
+ p.proxy_to(@app.blog.options.tag_template)
22
+
23
+ p.add_metadata do
24
+ @tag = tag
25
+ @articles = articles
26
+ end
27
+
28
+ p
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,24 +1,35 @@
1
+ require "middleman-core/templates"
2
+
1
3
  module Middleman
2
4
  module Blog
5
+
6
+ # A template that generates a blog-specific config.rb
7
+ # and a set of example templates for index, layout, tags, and calendar.
3
8
  class Template < Middleman::Templates::Base
9
+ class_option "css_dir",
10
+ :default => "stylesheets",
11
+ :desc => 'The path to the css files'
12
+ class_option "js_dir",
13
+ :default => "javascripts",
14
+ :desc => 'The path to the javascript files'
15
+ class_option "images_dir",
16
+ :default => "images",
17
+ :desc => 'The path to the image files'
18
+
4
19
  def self.source_root
5
- File.dirname(__FILE__)
20
+ File.join(File.dirname(__FILE__), 'template')
6
21
  end
7
22
 
8
23
  def build_scaffold
9
- template "template/config.tt", File.join(location, "config.rb")
10
- directory "template/source", File.join(location, "source")
24
+ template "config.tt", File.join(location, "config.rb")
25
+ directory "source", File.join(location, "source")
11
26
 
12
27
  empty_directory File.join(location, "source", options[:css_dir])
13
28
  empty_directory File.join(location, "source", options[:js_dir])
14
29
  empty_directory File.join(location, "source", options[:images_dir])
15
30
  end
16
-
17
- def generate_rack
18
- template "template/config.ru", File.join(location, "config.ru")
19
- end
20
31
  end
21
32
  end
22
33
  end
23
34
 
24
- Middleman::Templates.register(:blog, Middleman::Blog::Template)
35
+ Middleman::Templates.register(:blog, Middleman::Blog::Template)