middleman-blog 0.1.5 → 3.0.0.beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)