middleman-blog 4.0.3 → 4.2.0

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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +3 -0
  3. data/.github/workflows/ci.yml +25 -0
  4. data/.github/workflows/stale.yml +21 -0
  5. data/.gitignore +1 -3
  6. data/CHANGELOG.md +78 -60
  7. data/Gemfile +12 -26
  8. data/README.md +20 -30
  9. data/Rakefile +8 -14
  10. data/features/alias.feature +41 -0
  11. data/features/summary.feature +21 -2
  12. data/features/support/env.rb +2 -0
  13. data/features/support/time_steps.rb +9 -7
  14. data/features/tags.feature +4 -0
  15. data/fixtures/alias-app/config.rb +10 -0
  16. data/fixtures/alias-app/source/2024-03-14-pi-day.html.markdown +7 -0
  17. data/fixtures/alias-app/source/index.html +1 -0
  18. data/fixtures/alias-prefix-app/config.rb +11 -0
  19. data/fixtures/alias-prefix-app/source/blog/2024-01-15-prefix-test.html.markdown +7 -0
  20. data/fixtures/alias-prefix-app/source/index.html +1 -0
  21. data/fixtures/article-dirs-app/config-directory-indexes.rb +2 -0
  22. data/fixtures/article-dirs-app/config-permalink-with-dot.rb +2 -0
  23. data/fixtures/article-dirs-app/config.rb +2 -0
  24. data/fixtures/blog-sources-app/config.rb +2 -0
  25. data/fixtures/blog-sources-no-date-app/config.rb +2 -0
  26. data/fixtures/blog-sources-no-day-app/config.rb +2 -0
  27. data/fixtures/blog-sources-no-title-app/config.rb +2 -0
  28. data/fixtures/blog-sources-subdirs-app/config.rb +2 -0
  29. data/fixtures/calendar-and-tag-app/config-directory-indexes.rb +2 -0
  30. data/fixtures/calendar-and-tag-app/config.rb +2 -0
  31. data/fixtures/calendar-app/config-directory-indexes.rb +2 -0
  32. data/fixtures/calendar-app/config-only-year.rb +2 -0
  33. data/fixtures/calendar-app/config.rb +2 -0
  34. data/fixtures/calendar-multiblog-app/config.rb +2 -0
  35. data/fixtures/custom-article-template-app/config.rb +2 -0
  36. data/fixtures/custom-collections-app/config-blog-prefix.rb +2 -0
  37. data/fixtures/custom-collections-app/config-directory-indexes.rb +2 -0
  38. data/fixtures/custom-collections-app/config.rb +2 -0
  39. data/fixtures/custom-collections-multiblog-app/config.rb +2 -0
  40. data/fixtures/custom-collections-sources-app/config.rb +2 -0
  41. data/fixtures/custom-permalinks-app/config-directory-indexes.rb +2 -0
  42. data/fixtures/custom-permalinks-app/config.rb +2 -0
  43. data/fixtures/filename-date-app/config.rb +2 -0
  44. data/fixtures/future-date-app/config.rb +2 -0
  45. data/fixtures/indexes-app/config.rb +2 -0
  46. data/fixtures/lang-path-app/config.rb +2 -0
  47. data/fixtures/language-app/config.rb +3 -1
  48. data/fixtures/layouts-app/config.rb +2 -0
  49. data/fixtures/paginate-app/config-directory-indexes.rb +2 -0
  50. data/fixtures/paginate-app/config-paginate-off.rb +2 -0
  51. data/fixtures/paginate-app/config.rb +2 -0
  52. data/fixtures/paginate-multiblog-app/config.rb +2 -0
  53. data/fixtures/permalink-app/config.rb +2 -0
  54. data/fixtures/preview-app/config.rb +2 -0
  55. data/fixtures/published-app/config.rb +2 -0
  56. data/fixtures/summary-app/config.rb +2 -0
  57. data/fixtures/summary-app/source/layout.erb +6 -0
  58. data/fixtures/tags-app/config-directory-indexes.rb +2 -0
  59. data/fixtures/tags-app/config-filters.rb +2 -0
  60. data/fixtures/tags-app/config-no-tags.rb +2 -0
  61. data/fixtures/tags-app/config.rb +2 -0
  62. data/fixtures/tags-app/source/blog/2011-01-01-new-article.html.markdown +1 -1
  63. data/fixtures/tags-app/source/blog/2011-01-02-another-article.html.markdown +1 -0
  64. data/fixtures/tags-multiblog-app/config.rb +2 -0
  65. data/fixtures/time-zone-app/config.rb +2 -0
  66. data/lib/middleman-blog/alias_pages.rb +161 -0
  67. data/lib/middleman-blog/blog_article.rb +17 -11
  68. data/lib/middleman-blog/blog_data.rb +4 -8
  69. data/lib/middleman-blog/calendar_pages.rb +8 -6
  70. data/lib/middleman-blog/commands/article.rb +10 -6
  71. data/lib/middleman-blog/commands/article.tt +2 -2
  72. data/lib/middleman-blog/custom_pages.rb +3 -1
  73. data/lib/middleman-blog/extension.rb +32 -12
  74. data/lib/middleman-blog/helpers.rb +4 -2
  75. data/lib/middleman-blog/paginator.rb +3 -1
  76. data/lib/middleman-blog/tag_pages.rb +3 -1
  77. data/lib/middleman-blog/truncate_html.rb +21 -5
  78. data/lib/middleman-blog/uri_templates.rb +12 -6
  79. data/lib/middleman-blog/version.rb +3 -1
  80. data/lib/middleman-blog.rb +2 -0
  81. data/lib/middleman_extension.rb +2 -0
  82. data/middleman-blog.gemspec +15 -22
  83. data/spec/alias_spec.rb +116 -0
  84. data/spec/spec_helper.rb +3 -1
  85. data/spec/uri_templates_spec.rb +14 -0
  86. metadata +30 -34
  87. data/.rubocop.yml +0 -69
  88. data/.travis.yml +0 -30
  89. data/Gemfile-4.x +0 -38
  90. data/fixtures/default-template-app/Gemfile +0 -6
  91. data/fixtures/default-template-app/config.rb +0 -35
  92. data/fixtures/default-template-app/source/2013-04-01-new-article.html.markdown +0 -25
  93. data/fixtures/default-template-app/source/about-me.html.erb +0 -0
  94. data/fixtures/default-template-app/source/archives.html.erb +0 -10
  95. data/fixtures/default-template-app/source/index.html.erb +0 -11
  96. data/fixtures/default-template-app/source/javascripts/_zepto.pjax.js +0 -744
  97. data/fixtures/default-template-app/source/javascripts/app.js +0 -11
  98. data/fixtures/default-template-app/source/javascripts/modernizr.js +0 -1
  99. data/fixtures/default-template-app/source/layouts/layout.erb +0 -62
  100. data/fixtures/default-template-app/source/stylesheets/app.css.scss +0 -109
  101. /data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
  102. /data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'middleman-core/cli'
2
4
  require 'date'
3
5
  require 'middleman-blog/uri_templates'
@@ -65,7 +67,6 @@ module Middleman
65
67
  @content = options[:content] || ''
66
68
  @date = options[:date] ? ::Time.zone.parse(options[:date]) : Time.zone.now
67
69
  @locale = options[:locale] || (::I18n.default_locale if defined? ::I18n)
68
- @slug = safe_parameterize(title)
69
70
  @tags = options[:tags]&.split(/\s*,\s*/) || []
70
71
  @title = title
71
72
 
@@ -77,17 +78,20 @@ module Middleman
77
78
  end
78
79
 
79
80
  blog_inst = if options[:blog]
80
- app.extensions[:blog].find { |_key, instance| instance.options[:name] == options[:blog] }[ 1 ]
81
- else
82
- app.extensions[:blog].values.first
83
- end
81
+ app.extensions[:blog].find { |_key, instance| instance.options[:name] == options[:blog] }[ 1 ]
82
+ else
83
+ app.extensions[:blog].values.first
84
+ end
84
85
 
85
86
  unless blog_inst
86
87
  msg = 'Could not find an active blog instance'
87
- msg << " named #{options[:blog]}" if options[:blog]
88
+ msg = "#{msg} named #{options[:blog]}" if options[:blog]
88
89
  throw msg
89
90
  end
90
91
 
92
+ # Generate slug after we have access to blog options
93
+ @slug = safe_parameterize(title, preserve_underscores: blog_inst.options.preserve_underscores_in_slugs)
94
+
91
95
  path_template = blog_inst.data.source_template
92
96
  params = date_to_params(@date).merge(locale: @locale.to_s, title: @slug)
93
97
  article_path = apply_uri_template path_template, params
@@ -1,7 +1,7 @@
1
1
  ---
2
2
 
3
- title: <%= @title %>
4
- date: <%= @date.strftime('%F %R %Z') %>
3
+ title: "<%= %Q[#{@title}] %>"
4
+ date: <%= @date.strftime('%F %R %z') %>
5
5
  tags: <%= @tags.join(",") %>
6
6
 
7
7
  ---
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'middleman-blog/uri_templates'
2
4
 
3
5
  module Middleman
@@ -21,7 +23,7 @@ module Middleman
21
23
  #
22
24
  # @param [String] value
23
25
  def link(value)
24
- apply_uri_template @link_template, property => safe_parameterize(value)
26
+ apply_uri_template @link_template, property => safe_parameterize(value, preserve_underscores: @blog_controller.options.preserve_underscores_in_slugs)
25
27
  end
26
28
 
27
29
  def manipulate_resource_list(resources)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/time/zones'
2
4
  require 'middleman-blog/blog_data'
3
5
  require 'middleman-blog/blog_article'
@@ -39,8 +41,10 @@ module Middleman
39
41
  option :publish_future_dated, false, 'Whether articles with a date in the future should be considered published'
40
42
  option :custom_collections, {}, 'Hash of custom frontmatter properties to collect articles on and their options (link, template)'
41
43
  option :preserve_locale, false, 'Use the global Middleman I18n.locale instead of the lang in the article\'s frontmatter'
44
+ option :preserve_underscores_in_slugs, false, 'Whether to preserve underscores in article slugs instead of converting them to dashes'
42
45
  option :new_article_template, File.expand_path('commands/article.tt', __dir__), 'Path (relative to project root) to an ERb template that will be used to generate new articles from the "middleman article" command.'
43
46
  option :default_extension, '.markdown', 'Default template extension for articles (used by "middleman article")'
47
+ option :aliases, [], 'Array of URL patterns that should redirect to the main permalink (e.g., [":year-:month-:day-:title.html"])'
44
48
 
45
49
  # @return [BlogData] blog data for this blog, which has all information about the blog articles
46
50
  attr_reader :data
@@ -60,6 +64,9 @@ module Middleman
60
64
  # @return [Hash<CustomPages>] custom pages handlers for this blog, indexed by property name
61
65
  attr_reader :custom_pages
62
66
 
67
+ # @return [AliasPages] alias page handler for this blog
68
+ attr_reader :alias_pages
69
+
63
70
  # Helpers for use within templates and layouts.
64
71
  self.defined_helpers = [Middleman::Blog::Helpers]
65
72
 
@@ -79,18 +86,23 @@ module Middleman
79
86
  end
80
87
 
81
88
  # If "prefix" option is specified, all other paths are relative to it.
82
- if options.prefix
83
- options.prefix = "/#{options.prefix}" unless options.prefix.start_with? '/'
84
- options.permalink = File.join(options.prefix, options.permalink)
85
- options.sources = File.join(options.prefix, options.sources)
86
- options.taglink = File.join(options.prefix, options.taglink)
87
- options.year_link = File.join(options.prefix, options.year_link)
88
- options.month_link = File.join(options.prefix, options.month_link)
89
- options.day_link = File.join(options.prefix, options.day_link)
90
-
91
- options.custom_collections.each do |_key, opts|
92
- opts[:link] = File.join(options.prefix, opts[:link])
93
- end
89
+ return unless options.prefix
90
+
91
+ options.prefix = "/#{options.prefix}" unless options.prefix.start_with? '/'
92
+ options.permalink = File.join(options.prefix, options.permalink)
93
+ options.sources = File.join(options.prefix, options.sources)
94
+ options.taglink = File.join(options.prefix, options.taglink)
95
+ options.year_link = File.join(options.prefix, options.year_link)
96
+ options.month_link = File.join(options.prefix, options.month_link)
97
+ options.day_link = File.join(options.prefix, options.day_link)
98
+
99
+ options.custom_collections.each_value do |opts|
100
+ opts[:link] = File.join(options.prefix, opts[:link])
101
+ end
102
+
103
+ # Apply prefix to alias patterns if specified
104
+ unless options.aliases.nil? || options.aliases.empty?
105
+ options.aliases = options.aliases.map { |alias_pattern| File.join(options.prefix, alias_pattern) }
94
106
  end
95
107
  end
96
108
 
@@ -113,6 +125,8 @@ module Middleman
113
125
  @app.ignore(options.day_template) if options.day_template
114
126
  @app.ignore options.tag_template if options.tag_template
115
127
 
128
+ ActiveSupport.to_time_preserves_timezone = :zone
129
+
116
130
  # Make sure ActiveSupport's TimeZone stuff has something to work with,
117
131
  # allowing people to set their desired time zone via Time.zone or
118
132
  # set :time_zone
@@ -153,6 +167,12 @@ module Middleman
153
167
  @app.sitemap.register_resource_list_manipulator(:"blog_#{name}_paginate", @paginator)
154
168
  end
155
169
 
170
+ unless options.aliases.nil? || options.aliases.empty?
171
+ require 'middleman-blog/alias_pages'
172
+ @alias_pages = Blog::AliasPages.new(@app, self)
173
+ @app.sitemap.register_resource_list_manipulator(:"blog_#{name}_aliases", @alias_pages)
174
+ end
175
+
156
176
  logger.info "== Blog Sources: #{options.sources} (:prefix + :sources)"
157
177
  end
158
178
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Middleman
2
4
  module Blog
3
5
  # Blog-related helpers that are available to the Middleman application in +config.rb+ and in templates.
@@ -59,7 +61,7 @@ module Middleman
59
61
  # Determine whether the currently rendering template is a {BlogArticle}.
60
62
  # This can be useful in layouts and helpers.
61
63
  # @return [Boolean]
62
- def is_blog_article? # rubocop:disable Naming/PredicateName
64
+ def is_blog_article?
63
65
  !current_article.nil?
64
66
  end
65
67
 
@@ -67,7 +69,7 @@ module Middleman
67
69
  # @return [BlogArticle]
68
70
  def current_article
69
71
  article = current_resource
70
- article if article&.is_a?(BlogArticle)
72
+ article if article.is_a?(BlogArticle)
71
73
  end
72
74
 
73
75
  # Get a path to the given tag page, based on the +taglink+ blog setting.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'middleman-blog/uri_templates'
2
4
 
3
5
  module Middleman
@@ -149,7 +151,7 @@ module Middleman
149
151
  else
150
152
  page_url = apply_uri_template page_link, num: page_num
151
153
  index_re = %r{(^|/)#{Regexp.escape(@app.config[:index_file])}$}
152
- if res.path =~ index_re
154
+ if res.path&.match?(index_re)
153
155
  res.path.sub(index_re, "\\1#{page_url}/#{@app.config[:index_file]}")
154
156
  else
155
157
  res.path.sub(%r{(^|/)([^/]*)\.([^/]*)$}, "\\1\\2/#{page_url}.\\3")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'middleman-blog/uri_templates'
2
4
 
3
5
  module Middleman
@@ -31,7 +33,7 @@ module Middleman
31
33
  # @return [String] Safe Tag URL
32
34
  ##
33
35
  def link(tag)
34
- apply_uri_template @tag_link_template, tag: safe_parameterize(tag)
36
+ apply_uri_template @tag_link_template, tag: safe_parameterize(tag, preserve_underscores: @blog_controller.options.preserve_underscores_in_slugs)
35
37
  end
36
38
 
37
39
  ##
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'nokogiri'
3
5
  rescue LoadError
@@ -13,6 +15,20 @@ module TruncateHTML
13
15
  doc.inner_html
14
16
  end
15
17
 
18
+ def self.content_after_separator(text, separator)
19
+ text = text.encode('UTF-8') if text.respond_to?(:encode)
20
+ parts = text.split(separator, 2)
21
+
22
+ if parts.length >= 2
23
+ # Take the last part (which should be after the separator)
24
+ content_after = parts.last
25
+ doc = Nokogiri::HTML::DocumentFragment.parse content_after
26
+ doc.inner_html
27
+ else
28
+ text
29
+ end
30
+ end
31
+
16
32
  def self.truncate_at_length(text, max_length, ellipsis = '...')
17
33
  ellipsis_length = ellipsis.length
18
34
  text = text.encode('UTF-8') if text.respond_to?(:encode)
@@ -48,7 +64,7 @@ module NokogiriTruncator
48
64
  module TextNode
49
65
  def truncate(max_length, ellipsis)
50
66
  # Don't break in the middle of a word
51
- trimmed_content = content.match(/(.{1,#{max_length}}[\w]*)/m).to_s
67
+ trimmed_content = content.match(/(.{1,#{max_length}}\w*)/m).to_s
52
68
  trimmed_content << ellipsis if trimmed_content.length < content.length
53
69
 
54
70
  Nokogiri::XML::Text.new(trimmed_content, parent)
@@ -63,7 +79,7 @@ module NokogiriTruncator
63
79
  end
64
80
  end
65
81
 
66
- Nokogiri::HTML::DocumentFragment.send(:include, NokogiriTruncator::NodeWithChildren)
67
- Nokogiri::XML::Element.send(:include, NokogiriTruncator::NodeWithChildren)
68
- Nokogiri::XML::Text.send(:include, NokogiriTruncator::TextNode)
69
- Nokogiri::XML::Comment.send(:include, NokogiriTruncator::CommentNode)
82
+ Nokogiri::HTML::DocumentFragment.include NokogiriTruncator::NodeWithChildren
83
+ Nokogiri::XML::Element.include NokogiriTruncator::NodeWithChildren
84
+ Nokogiri::XML::Text.include NokogiriTruncator::TextNode
85
+ Nokogiri::XML::Comment.include NokogiriTruncator::CommentNode
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'addressable/template'
4
+ require 'active_support/all'
2
5
  require 'middleman-core/util'
3
6
  require 'active_support/inflector'
4
7
  require 'active_support/inflector/transliterate'
@@ -51,13 +54,13 @@ module Middleman
51
54
  # Reimplementation of this, preserves un-transliterate-able multibyte chars.
52
55
  #
53
56
  # @see http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize
54
- def safe_parameterize(str, sep = '-')
57
+ def safe_parameterize(str, separator: '-', preserve_underscores: false)
55
58
  # Remove ending ?
56
59
  str = str.to_s.gsub(/\?$/, '')
57
60
 
58
61
  # Reimplementation of http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize that preserves un-transliterate-able multibyte chars.
59
62
  parameterized_string = ::ActiveSupport::Inflector.transliterate(str.to_s).downcase
60
- parameterized_string.gsub!(/[^a-z0-9\-_\?]+/, sep)
63
+ parameterized_string.gsub!(/[^a-z0-9\-_?]+/, separator)
61
64
 
62
65
  # Check for multibytes and sub back in
63
66
  parameterized_string.chars.to_a.each_with_index do |char, i|
@@ -66,17 +69,20 @@ module Middleman
66
69
  parameterized_string[i] = str[i]
67
70
  end
68
71
 
69
- re_sep = ::Regexp.escape(sep)
72
+ re_sep = ::Regexp.escape(separator)
70
73
 
71
74
  # No more than one of the separator in a row.
72
- parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
75
+ parameterized_string.gsub!(/#{re_sep}{2,}/, separator)
73
76
 
74
77
  # Remove leading/trailing separator.
75
78
  parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/, '')
76
- parameterized_string.tr!('_', '-')
79
+
80
+ # Replace all _ with - (unless preserve_underscores is true)
81
+ parameterized_string.tr!('_', '-') unless preserve_underscores
82
+
83
+ # Delete all ?
77
84
  parameterized_string.delete!('?')
78
85
 
79
- # Replace all ?
80
86
  parameterized_string
81
87
  end
82
88
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Middleman
2
4
  module Blog
3
- VERSION = '4.0.3'.freeze
5
+ VERSION = '4.2.0'
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'middleman-core'
2
4
  require 'middleman-blog/version'
3
5
 
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'middleman-blog'
@@ -1,25 +1,18 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path( "../lib", __FILE__ )
3
- require "middleman-blog/version"
1
+ require "./lib/middleman-blog/version"
4
2
 
5
3
  Gem::Specification.new do | s |
6
-
7
- s.name = "middleman-blog"
8
- s.version = Middleman::Blog::VERSION
9
- s.platform = Gem::Platform::RUBY
10
- s.authors = [ "Thomas Reynolds", "Ben Hollis", "Ian Warner" ]
11
- s.email = [ "me@tdreyno.com", "ben@benhollis.net", "ian.warner@drykiss.com" ]
12
- s.homepage = "https://github.com/middleman/middleman-blog"
13
- s.summary = %q{ Blog engine for Middleman }
14
- s.description = %q{ Blog engine for Middleman }
15
- s.license = "MIT"
16
- s.files = `git ls-files -z`.split( "\0" )
17
- s.test_files = `git ls-files -z -- {fixtures,features}/*`.split( "\0" )
18
- s.require_paths = [ "lib" ]
19
- s.required_ruby_version = '>= 2.3.0'
20
-
21
- s.add_dependency( "middleman-core", [ ">= 4.0.0" ] )
22
- s.add_dependency( "tzinfo", [ ">= 0.3.0" ] )
23
- s.add_dependency( "addressable", [ "~> 2.3" ] )
24
-
4
+ s.name = "middleman-blog"
5
+ s.version = Middleman::Blog::VERSION
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = [ "Thomas Reynolds", "Ben Hollis", "Ian Warner" ]
8
+ s.email = [ "me@tdreyno.com", "ben@benhollis.net", "ian.warner@drykiss.com" ]
9
+ s.homepage = "https://github.com/middleman/middleman-blog"
10
+ s.summary = %q{ Blog engine for Middleman }
11
+ s.description = %q{ Blog engine for Middleman }
12
+ s.license = "MIT"
13
+ s.files = `git ls-files -z`.split( "\0" )
14
+ s.test_files = `git ls-files -z -- {fixtures,features}/*`.split( "\0" )
15
+ s.add_dependency("middleman-core", ">= 4.0.0")
16
+ s.add_dependency("tzinfo", ">= 0.3.0")
17
+ s.add_dependency("addressable", "~> 2.3")
25
18
  end
@@ -0,0 +1,116 @@
1
+ require 'middleman-blog/alias_pages'
2
+ require 'middleman-blog/uri_templates'
3
+
4
+ describe 'Middleman::Blog::AliasPages' do
5
+ include Middleman::Blog::UriTemplates
6
+
7
+ let(:mock_app) { double('app') }
8
+ let(:mock_sitemap) { double('sitemap') }
9
+ let(:mock_blog_controller) { double('blog_controller') }
10
+ let(:mock_blog_data) { double('blog_data') }
11
+ let(:mock_article) { double('article') }
12
+ let(:mock_options) { double('options') }
13
+
14
+ before do
15
+ allow(mock_app).to receive(:sitemap).and_return(mock_sitemap)
16
+ allow(mock_blog_controller).to receive(:data).and_return(mock_blog_data)
17
+ allow(mock_blog_controller).to receive(:options).and_return(mock_options)
18
+ allow(mock_article).to receive(:destination_path).and_return('2024/03/14/pi-day.html')
19
+ allow(mock_article).to receive(:date).and_return(Date.new(2024, 3, 14))
20
+ allow(mock_article).to receive(:slug).and_return('pi-day')
21
+ allow(mock_article).to receive(:lang).and_return(:en)
22
+ allow(mock_article).to receive(:locale).and_return(:en)
23
+ allow(mock_article).to receive(:metadata).and_return({ page: {} })
24
+ end
25
+
26
+ describe 'alias URL generation' do
27
+ let(:alias_patterns) { [':year-:month-:day-:title.html', ':year/:month-:day-:title'] }
28
+
29
+ before do
30
+ allow(mock_options).to receive(:aliases).and_return(alias_patterns)
31
+ allow(mock_blog_data).to receive(:articles).and_return([mock_article])
32
+ end
33
+
34
+ context 'when testing alias path generation' do
35
+ let(:alias_pages) { Middleman::Blog::AliasPages.new(mock_app, mock_blog_controller) }
36
+
37
+ it 'generates correct alias paths from patterns' do
38
+ template1 = uri_template(':year-:month-:day-:title.html')
39
+ template2 = uri_template(':year/:month-:day-:title')
40
+
41
+ # Test path generation directly
42
+ alias_path1 = alias_pages.send(:generate_alias_path, template1, mock_article)
43
+ alias_path2 = alias_pages.send(:generate_alias_path, template2, mock_article)
44
+
45
+ expect(alias_path1).to eq('2024-03-14-pi-day.html')
46
+ expect(alias_path2).to eq('2024/03-14-pi-day')
47
+ end
48
+
49
+ it 'filters out aliases that match main permalink' do
50
+ # Test with an alias pattern that would match the main permalink
51
+ template = uri_template(':year/:month/:day/:title.html')
52
+
53
+ alias_path = alias_pages.send(:generate_alias_path, template, mock_article)
54
+ expect(alias_path).to eq('2024/03/14/pi-day.html')
55
+
56
+ # This would be filtered out in manipulate_resource_list because it matches destination_path
57
+ expect(alias_path).to eq(mock_article.destination_path)
58
+ end
59
+ end
60
+
61
+ context 'when testing permalink options generation' do
62
+ let(:alias_pages) { Middleman::Blog::AliasPages.new(mock_app, mock_blog_controller) }
63
+
64
+ it 'generates correct permalink options from article data' do
65
+ params = alias_pages.send(:permalink_options, mock_article)
66
+
67
+ expect(params[:year]).to eq('2024')
68
+ expect(params[:month]).to eq('03')
69
+ expect(params[:day]).to eq('14')
70
+ expect(params[:title]).to eq('pi-day')
71
+ expect(params[:lang]).to eq('en')
72
+ expect(params[:locale]).to eq('en')
73
+ end
74
+ end
75
+ end
76
+
77
+ describe 'empty aliases configuration' do
78
+ before do
79
+ allow(mock_options).to receive(:aliases).and_return([])
80
+ allow(mock_blog_data).to receive(:articles).and_return([mock_article])
81
+ end
82
+
83
+ it 'returns original resources when aliases array is empty' do
84
+ alias_pages = Middleman::Blog::AliasPages.new(mock_app, mock_blog_controller)
85
+ resources = ['existing_resource']
86
+ result = alias_pages.manipulate_resource_list(resources)
87
+
88
+ # Should return the same resources since no aliases are configured
89
+ expect(result).to eq(resources)
90
+ end
91
+ end
92
+
93
+ describe 'alias pattern handling' do
94
+ before do
95
+ allow(mock_blog_data).to receive(:articles).and_return([mock_article])
96
+ end
97
+
98
+ it 'handles multiple alias patterns' do
99
+ patterns = [':year-:month-:day-:title.html', ':year/:month-:day-:title', 'archive/:title']
100
+ allow(mock_options).to receive(:aliases).and_return(patterns)
101
+
102
+ alias_pages = Middleman::Blog::AliasPages.new(mock_app, mock_blog_controller)
103
+
104
+ # Test that all patterns are converted to templates
105
+ expect(alias_pages.instance_variable_get(:@alias_templates).length).to eq(3)
106
+ end
107
+
108
+ it 'handles nil alias patterns gracefully' do
109
+ allow(mock_options).to receive(:aliases).and_return(nil)
110
+
111
+ expect {
112
+ Middleman::Blog::AliasPages.new(mock_app, mock_blog_controller)
113
+ }.not_to raise_error
114
+ end
115
+ end
116
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'simplecov'
2
- SimpleCov.root(File.expand_path(File.dirname(__FILE__) + '/..'))
4
+ SimpleCov.root(File.expand_path("#{File.dirname(__FILE__)}/.."))
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'middleman-blog/uri_templates'
2
4
 
3
5
  describe 'Middleman::Blog::UriTemplates' do
@@ -24,6 +26,18 @@ describe 'Middleman::Blog::UriTemplates' do
24
26
  it 'can handle numbers' do
25
27
  expect(safe_parameterize(1)).to eq '1'
26
28
  end
29
+
30
+ it 'converts underscores to dashes by default' do
31
+ expect(safe_parameterize('name_of_article')).to eq 'name-of-article'
32
+ end
33
+
34
+ it 'can preserve underscores when requested' do
35
+ expect(safe_parameterize('name_of_article', preserve_underscores: true)).to eq 'name_of_article'
36
+ end
37
+
38
+ it 'still works with mixed content when preserving underscores' do
39
+ expect(safe_parameterize('Some MIXED_content', preserve_underscores: true)).to eq 'some-mixed_content'
40
+ end
27
41
  end
28
42
 
29
43
  describe 'extract_params' do