bunto 3.0.0 → 3.2.1

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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +124 -76
  3. data/README.markdown +49 -12
  4. data/{bin → exe}/bunto +18 -14
  5. data/lib/bunto.rb +83 -78
  6. data/lib/bunto/cleaner.rb +10 -8
  7. data/lib/bunto/collection.rb +33 -17
  8. data/lib/bunto/command.rb +19 -13
  9. data/lib/bunto/commands/build.rb +22 -14
  10. data/lib/bunto/commands/clean.rb +9 -8
  11. data/lib/bunto/commands/doctor.rb +10 -8
  12. data/lib/bunto/commands/help.rb +4 -3
  13. data/lib/bunto/commands/new.rb +30 -21
  14. data/lib/bunto/commands/new_theme.rb +36 -0
  15. data/lib/bunto/commands/serve.rb +26 -20
  16. data/lib/bunto/commands/serve/servlet.rb +4 -5
  17. data/lib/bunto/configuration.rb +187 -125
  18. data/lib/bunto/converters/markdown.rb +19 -9
  19. data/lib/bunto/converters/markdown/kramdown_parser.rb +12 -5
  20. data/lib/bunto/converters/markdown/rdiscount_parser.rb +4 -4
  21. data/lib/bunto/converters/markdown/redcarpet_parser.rb +90 -84
  22. data/lib/bunto/convertible.rb +38 -25
  23. data/lib/bunto/deprecator.rb +11 -6
  24. data/lib/bunto/desktop.ini +2 -0
  25. data/lib/bunto/document.rb +53 -51
  26. data/lib/bunto/drops/bunto_drop.rb +12 -0
  27. data/lib/bunto/drops/document_drop.rb +40 -5
  28. data/lib/bunto/drops/drop.rb +49 -10
  29. data/lib/bunto/drops/excerpt_drop.rb +15 -0
  30. data/lib/bunto/drops/site_drop.rb +4 -2
  31. data/lib/bunto/drops/url_drop.rb +4 -4
  32. data/lib/bunto/entry_filter.rb +64 -19
  33. data/lib/bunto/errors.rb +6 -3
  34. data/lib/bunto/excerpt.rb +4 -6
  35. data/lib/bunto/external.rb +4 -4
  36. data/lib/bunto/filters.rb +72 -39
  37. data/lib/bunto/frontmatter_defaults.rb +45 -38
  38. data/lib/bunto/hooks.rb +21 -21
  39. data/lib/bunto/layout.rb +4 -8
  40. data/lib/bunto/liquid_renderer.rb +14 -3
  41. data/lib/bunto/liquid_renderer/file.rb +5 -1
  42. data/lib/bunto/liquid_renderer/table.rb +11 -11
  43. data/lib/bunto/log_adapter.rb +2 -2
  44. data/lib/bunto/page.rb +10 -10
  45. data/lib/bunto/plugin.rb +5 -5
  46. data/lib/bunto/plugin_manager.rb +12 -8
  47. data/lib/bunto/publisher.rb +1 -1
  48. data/lib/bunto/reader.rb +11 -7
  49. data/lib/bunto/readers/data_reader.rb +9 -9
  50. data/lib/bunto/readers/layout_reader.rb +7 -7
  51. data/lib/bunto/readers/page_reader.rb +3 -1
  52. data/lib/bunto/readers/post_reader.rb +9 -10
  53. data/lib/bunto/readers/static_file_reader.rb +3 -1
  54. data/lib/bunto/regenerator.rb +50 -28
  55. data/lib/bunto/related_posts.rb +1 -1
  56. data/lib/bunto/renderer.rb +33 -23
  57. data/lib/bunto/site.rb +94 -51
  58. data/lib/bunto/static_file.rb +33 -26
  59. data/lib/bunto/stevenson.rb +6 -5
  60. data/lib/bunto/tags/highlight.rb +50 -35
  61. data/lib/bunto/tags/include.rb +42 -31
  62. data/lib/bunto/tags/link.rb +11 -4
  63. data/lib/bunto/tags/post_url.rb +8 -7
  64. data/lib/bunto/theme.rb +10 -8
  65. data/lib/bunto/theme_builder.rb +117 -0
  66. data/lib/bunto/url.rb +21 -14
  67. data/lib/bunto/utils.rb +57 -28
  68. data/lib/bunto/utils/ansi.rb +9 -9
  69. data/lib/bunto/utils/platforms.rb +2 -2
  70. data/lib/bunto/version.rb +1 -1
  71. data/lib/site_template/_config.yml +3 -1
  72. data/lib/site_template/_posts/0000-00-00-welcome-to-bunto.markdown.erb +3 -3
  73. data/lib/site_template/about.md +3 -3
  74. data/lib/site_template/css/main.scss +3 -17
  75. data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -0
  76. data/lib/theme_template/Gemfile +2 -0
  77. data/lib/theme_template/LICENSE.txt.erb +21 -0
  78. data/lib/theme_template/README.md.erb +48 -0
  79. data/lib/theme_template/_layouts/default.html +1 -0
  80. data/lib/theme_template/_layouts/page.html +5 -0
  81. data/lib/theme_template/_layouts/post.html +5 -0
  82. data/lib/theme_template/example/_config.yml.erb +1 -0
  83. data/lib/theme_template/example/_post.md +12 -0
  84. data/lib/theme_template/example/index.html +14 -0
  85. data/lib/theme_template/example/style.scss +7 -0
  86. data/lib/theme_template/gitignore.erb +4 -0
  87. data/lib/theme_template/theme.gemspec.erb +18 -0
  88. metadata +40 -19
  89. data/lib/site_template/_includes/footer.html +0 -38
  90. data/lib/site_template/_includes/head.html +0 -12
  91. data/lib/site_template/_includes/header.html +0 -27
  92. data/lib/site_template/_includes/icon-github.html +0 -1
  93. data/lib/site_template/_includes/icon-github.svg +0 -1
  94. data/lib/site_template/_includes/icon-twitter.html +0 -1
  95. data/lib/site_template/_includes/icon-twitter.svg +0 -1
  96. data/lib/site_template/_layouts/default.html +0 -20
  97. data/lib/site_template/_layouts/page.html +0 -14
  98. data/lib/site_template/_layouts/post.html +0 -15
  99. data/lib/site_template/_sass/_base.scss +0 -200
  100. data/lib/site_template/_sass/_layout.scss +0 -242
  101. data/lib/site_template/_sass/_syntax-highlighting.scss +0 -71
@@ -8,28 +8,15 @@ module Bunto
8
8
  # forms: name, name=value, or name="<quoted list>"
9
9
  #
10
10
  # <quoted list> is a space-separated list of numbers
11
- SYNTAX = /^([a-zA-Z0-9.+#-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$/
11
+ SYNTAX = %r!^([a-zA-Z0-9.+#-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$!
12
12
 
13
13
  def initialize(tag_name, markup, tokens)
14
14
  super
15
15
  if markup.strip =~ SYNTAX
16
16
  @lang = Regexp.last_match(1).downcase
17
- @highlight_options = {}
18
- if defined?(Regexp.last_match(2)) && Regexp.last_match(2) != ''
19
- # Split along 3 possible forms -- key="<quoted list>", key=value, or key
20
- Regexp.last_match(2).scan(/(?:\w="[^"]*"|\w=\w|\w)+/) do |opt|
21
- key, value = opt.split('=')
22
- # If a quoted list, convert to array
23
- if value && value.include?("\"")
24
- value.delete!('"')
25
- value = value.split
26
- end
27
- @highlight_options[key.to_sym] = value || true
28
- end
29
- end
30
- @highlight_options[:linenos] = "inline" if @highlight_options.key?(:linenos) && @highlight_options[:linenos] == true
17
+ @highlight_options = parse_options(Regexp.last_match(2))
31
18
  else
32
- raise SyntaxError.new <<-eos
19
+ raise SyntaxError, <<-eos
33
20
  Syntax Error in tag 'highlight' while parsing the following markup:
34
21
 
35
22
  #{markup}
@@ -42,15 +29,15 @@ eos
42
29
  def render(context)
43
30
  prefix = context["highlighter_prefix"] || ""
44
31
  suffix = context["highlighter_suffix"] || ""
45
- code = super.to_s.gsub(/\A(\n|\r)+|(\n|\r)+\z/, '')
32
+ code = super.to_s.gsub(%r!\A(\n|\r)+|(\n|\r)+\z!, "")
46
33
 
47
34
  is_safe = !!context.registers[:site].safe
48
35
 
49
36
  output =
50
37
  case context.registers[:site].highlighter
51
- when 'pygments'
38
+ when "pygments"
52
39
  render_pygments(code, is_safe)
53
- when 'rouge'
40
+ when "rouge"
54
41
  render_rouge(code)
55
42
  else
56
43
  render_codehighlighter(code)
@@ -66,7 +53,7 @@ eos
66
53
  [:startinline, opts.fetch(:startinline, nil)],
67
54
  [:hl_lines, opts.fetch(:hl_lines, nil)],
68
55
  [:linenos, opts.fetch(:linenos, nil)],
69
- [:encoding, opts.fetch(:encoding, 'utf-8')],
56
+ [:encoding, opts.fetch(:encoding, "utf-8")],
70
57
  [:cssclass, opts.fetch(:cssclass, nil)]
71
58
  ].reject { |f| f.last.nil? }]
72
59
  else
@@ -74,8 +61,30 @@ eos
74
61
  end
75
62
  end
76
63
 
64
+ private
65
+
66
+ def parse_options(input)
67
+ options = {}
68
+ unless input.empty?
69
+ # Split along 3 possible forms -- key="<quoted list>", key=value, or key
70
+ input.scan(%r!(?:\w="[^"]*"|\w=\w|\w)+!) do |opt|
71
+ key, value = opt.split("=")
72
+ # If a quoted list, convert to array
73
+ if value && value.include?("\"")
74
+ value.delete!('"')
75
+ value = value.split
76
+ end
77
+ options[key.to_sym] = value || true
78
+ end
79
+ end
80
+ if options.key?(:linenos) && options[:linenos] == true
81
+ options[:linenos] = "inline"
82
+ end
83
+ options
84
+ end
85
+
77
86
  def render_pygments(code, is_safe)
78
- Bunto::External.require_with_graceful_fail('pygments')
87
+ Bunto::External.require_with_graceful_fail("pygments")
79
88
 
80
89
  highlighted_code = Pygments.highlight(
81
90
  code,
@@ -84,22 +93,27 @@ eos
84
93
  )
85
94
 
86
95
  if highlighted_code.nil?
87
- Bunto.logger.error "There was an error highlighting your code:"
88
- puts
89
- Bunto.logger.error code
90
- puts
91
- Bunto.logger.error "While attempting to convert the above code, Pygments.rb" \
92
- " returned an unacceptable value."
93
- Bunto.logger.error "This is usually a timeout problem solved by running `bunto build` again."
94
- raise ArgumentError.new("Pygments.rb returned an unacceptable value when attempting to highlight some code.")
96
+ Bunto.logger.error <<eos
97
+ There was an error highlighting your code:
98
+
99
+ #{code}
100
+
101
+ While attempting to convert the above code, Pygments.rb returned an unacceptable value.
102
+ This is usually a timeout problem solved by running `bunto build` again.
103
+ eos
104
+ raise ArgumentError, "Pygments.rb returned an unacceptable value "\
105
+ "when attempting to highlight some code."
95
106
  end
96
107
 
97
- highlighted_code.sub('<div class="highlight"><pre>', '').sub('</pre></div>', '')
108
+ highlighted_code.sub('<div class="highlight"><pre>', "").sub("</pre></div>", "")
98
109
  end
99
110
 
100
111
  def render_rouge(code)
101
- Bunto::External.require_with_graceful_fail('rouge')
102
- formatter = Rouge::Formatters::HTML.new(:line_numbers => @highlight_options[:linenos], :wrap => false)
112
+ Bunto::External.require_with_graceful_fail("rouge")
113
+ formatter = Rouge::Formatters::HTML.new(
114
+ :line_numbers => @highlight_options[:linenos],
115
+ :wrap => false
116
+ )
103
117
  lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
104
118
  formatter.format(lexer.lex(code))
105
119
  end
@@ -110,13 +124,14 @@ eos
110
124
 
111
125
  def add_code_tag(code)
112
126
  code_attributes = [
113
- "class=\"language-#{@lang.to_s.tr('+', '-')}\"",
127
+ "class=\"language-#{@lang.to_s.tr("+", "-")}\"",
114
128
  "data-lang=\"#{@lang}\""
115
129
  ].join(" ")
116
- "<figure class=\"highlight\"><pre><code #{code_attributes}>#{code.chomp}</code></pre></figure>"
130
+ "<figure class=\"highlight\"><pre><code #{code_attributes}>"\
131
+ "#{code.chomp}</code></pre></figure>"
117
132
  end
118
133
  end
119
134
  end
120
135
  end
121
136
 
122
- Liquid::Template.register_tag('highlight', Bunto::Tags::HighlightBlock)
137
+ Liquid::Template.register_tag("highlight", Bunto::Tags::HighlightBlock)
@@ -12,17 +12,23 @@ module Bunto
12
12
  end
13
13
 
14
14
  class IncludeTag < Liquid::Tag
15
- VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
16
- VARIABLE_SYNTAX = /(?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)(?<params>.*)/
15
+ VALID_SYNTAX = %r!
16
+ ([\w-]+)\s*=\s*
17
+ (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))
18
+ !x
19
+ VARIABLE_SYNTAX = %r!
20
+ (?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)
21
+ (?<params>.*)
22
+ !x
17
23
 
18
24
  def initialize(tag_name, markup, tokens)
19
25
  super
20
26
  matched = markup.strip.match(VARIABLE_SYNTAX)
21
27
  if matched
22
- @file = matched['variable'].strip
23
- @params = matched['params'].strip
28
+ @file = matched["variable"].strip
29
+ @params = matched["params"].strip
24
30
  else
25
- @file, @params = markup.strip.split(' ', 2)
31
+ @file, @params = markup.strip.split(%r!\s+!, 2)
26
32
  end
27
33
  validate_params if @params
28
34
  @tag_name = tag_name
@@ -36,13 +42,13 @@ module Bunto
36
42
  params = {}
37
43
  markup = @params
38
44
 
39
- while match = VALID_SYNTAX.match(markup) do
45
+ while (match = VALID_SYNTAX.match(markup))
40
46
  markup = markup[match.end(0)..-1]
41
47
 
42
48
  value = if match[2]
43
- match[2].gsub(/\\"/, '"')
49
+ match[2].gsub(%r!\\"!, '"')
44
50
  elsif match[3]
45
- match[3].gsub(/\\'/, "'")
51
+ match[3].gsub(%r!\\'!, "'")
46
52
  elsif match[4]
47
53
  context[match[4]]
48
54
  end
@@ -53,8 +59,8 @@ module Bunto
53
59
  end
54
60
 
55
61
  def validate_file_name(file)
56
- if file !~ /^[a-zA-Z0-9_\/\.-]+$/ || file =~ /\.\// || file =~ /\/\./
57
- raise ArgumentError.new <<-eos
62
+ if file !~ %r!^[a-zA-Z0-9_/\.-]+$! || file =~ %r!\./! || file =~ %r!/\.!
63
+ raise ArgumentError, <<-eos
58
64
  Invalid syntax for include tag. File contains invalid characters or sequences:
59
65
 
60
66
  #{file}
@@ -68,9 +74,9 @@ eos
68
74
  end
69
75
 
70
76
  def validate_params
71
- full_valid_syntax = Regexp.compile('\A\s*(?:' + VALID_SYNTAX.to_s + '(?=\s|\z)\s*)*\z')
77
+ full_valid_syntax = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!
72
78
  unless @params =~ full_valid_syntax
73
- raise ArgumentError.new <<-eos
79
+ raise ArgumentError, <<-eos
74
80
  Invalid syntax for include tag:
75
81
 
76
82
  #{@params}
@@ -91,7 +97,10 @@ eos
91
97
  # Render the variable if required
92
98
  def render_variable(context)
93
99
  if @file.match(VARIABLE_SYNTAX)
94
- partial = context.registers[:site].liquid_renderer.file("(variable)").parse(@file)
100
+ partial = context.registers[:site]
101
+ .liquid_renderer
102
+ .file("(variable)")
103
+ .parse(@file)
95
104
  partial.render!(context)
96
105
  end
97
106
  end
@@ -106,9 +115,9 @@ eos
106
115
  path = File.join(dir, file)
107
116
  return path if valid_include_file?(path, dir, safe)
108
117
  end
109
- raise IOError, "Could not locate the included file '#{file}' in any of #{includes_dirs}." \
110
- " Ensure it exists in one of those directories and, if it is a symlink, " \
111
- "does not point outside your site source."
118
+ raise IOError, "Could not locate the included file '#{file}' in any of "\
119
+ "#{includes_dirs}. Ensure it exists in one of those directories and, "\
120
+ "if it is a symlink, does not point outside your site source."
112
121
  end
113
122
 
114
123
  def render(context)
@@ -120,24 +129,23 @@ eos
120
129
  path = locate_include_file(context, file, site.safe)
121
130
  return unless path
122
131
 
123
- # Add include to dependency tree
132
+ add_include_to_dependency(site, path, context)
133
+
134
+ partial = load_cached_partial(path, context)
135
+
136
+ context.stack do
137
+ context["include"] = parse_params(context) if @params
138
+ partial.render!(context)
139
+ end
140
+ end
141
+
142
+ def add_include_to_dependency(site, path, context)
124
143
  if context.registers[:page] && context.registers[:page].key?("path")
125
144
  site.regenerator.add_dependency(
126
145
  site.in_source_dir(context.registers[:page]["path"]),
127
146
  path
128
147
  )
129
148
  end
130
-
131
- #begin
132
- partial = load_cached_partial(path, context)
133
-
134
- context.stack do
135
- context['include'] = parse_params(context) if @params
136
- partial.render!(context)
137
- end
138
- #rescue => e
139
- #raise IncludeTagError.new e.message, path
140
- #end
141
149
  end
142
150
 
143
151
  def load_cached_partial(path, context)
@@ -147,7 +155,10 @@ eos
147
155
  if cached_partial.key?(path)
148
156
  cached_partial[path]
149
157
  else
150
- cached_partial[path] = context.registers[:site].liquid_renderer.file(path).parse(read_file(path, context))
158
+ cached_partial[path] = context.registers[:site]
159
+ .liquid_renderer
160
+ .file(path)
161
+ .parse(read_file(path, context))
151
162
  end
152
163
  end
153
164
 
@@ -188,5 +199,5 @@ eos
188
199
  end
189
200
  end
190
201
 
191
- Liquid::Template.register_tag('include', Bunto::Tags::IncludeTag)
192
- Liquid::Template.register_tag('include_relative', Bunto::Tags::IncludeRelativeTag)
202
+ Liquid::Template.register_tag("include", Bunto::Tags::IncludeTag)
203
+ Liquid::Template.register_tag("include_relative", Bunto::Tags::IncludeRelativeTag)
@@ -1,7 +1,11 @@
1
1
  module Bunto
2
2
  module Tags
3
3
  class Link < Liquid::Tag
4
- TagName = 'link'
4
+ class << self
5
+ def tag_name
6
+ self.name.split("::").last.downcase
7
+ end
8
+ end
5
9
 
6
10
  def initialize(tag_name, relative_path, tokens)
7
11
  super
@@ -16,11 +20,14 @@ module Bunto
16
20
  return document.url if document.relative_path == @relative_path
17
21
  end
18
22
 
19
- raise ArgumentError, "Could not find document '#{@relative_path}' in tag '#{TagName}'.\n\n" \
20
- "Make sure the document exists and the path is correct."
23
+ raise ArgumentError, <<eos
24
+ Could not find document '#{@relative_path}' in tag '#{self.class.tag_name}'.
25
+
26
+ Make sure the document exists and the path is correct.
27
+ eos
21
28
  end
22
29
  end
23
30
  end
24
31
  end
25
32
 
26
- Liquid::Template.register_tag(Bunto::Tags::Link::TagName, Bunto::Tags::Link)
33
+ Liquid::Template.register_tag(Bunto::Tags::Link.tag_name, Bunto::Tags::Link)
@@ -1,20 +1,20 @@
1
1
  module Bunto
2
2
  module Tags
3
3
  class PostComparer
4
- MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)$/
4
+ MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!
5
5
 
6
6
  attr_reader :path, :date, :slug, :name
7
7
 
8
8
  def initialize(name)
9
9
  @name = name
10
10
 
11
- all, @path, @date, @slug = *name.sub(/^\//, "").match(MATCHER)
11
+ all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER)
12
12
  unless all
13
13
  raise Bunto::Errors::InvalidPostNameError,
14
14
  "'#{name}' does not contain valid date and/or title."
15
15
  end
16
16
 
17
- @name_regex = /^#{path}#{date}-#{slug}\.[^.]+/
17
+ @name_regex = %r!^#{path}#{date}-#{slug}\.[^.]+!
18
18
  end
19
19
 
20
20
  def post_date
@@ -42,9 +42,9 @@ module Bunto
42
42
  def post_slug(other)
43
43
  path = other.basename.split("/")[0...-1].join("/")
44
44
  if path.nil? || path == ""
45
- other.data['slug']
45
+ other.data["slug"]
46
46
  else
47
- path + '/' + other.data['slug']
47
+ path + "/" + other.data["slug"]
48
48
  end
49
49
  end
50
50
  end
@@ -78,7 +78,8 @@ eos
78
78
 
79
79
  site.posts.docs.each do |p|
80
80
  next unless @post.deprecated_equality p
81
- Bunto::Deprecator.deprecation_message "A call to '{{ post_url #{@post.name} }}' did not match " \
81
+ Bunto::Deprecator.deprecation_message "A call to "\
82
+ "'{{ post_url #{@post.name} }}' did not match " \
82
83
  "a post using the new matching method of checking name " \
83
84
  "(path-date-slug) equality. Please make sure that you " \
84
85
  "change this tag to match the post's name exactly."
@@ -95,4 +96,4 @@ eos
95
96
  end
96
97
  end
97
98
 
98
- Liquid::Template.register_tag('post_url', Bunto::Tags::PostUrl)
99
+ Liquid::Template.register_tag("post_url", Bunto::Tags::PostUrl)
@@ -10,7 +10,11 @@ module Bunto
10
10
  end
11
11
 
12
12
  def root
13
- @root ||= gemspec.full_gem_path
13
+ # Must use File.realpath to resolve symlinks created by rbenv
14
+ # Otherwise, Bunto.sanitized path with prepend the unresolved root
15
+ @root ||= File.realpath(gemspec.full_gem_path)
16
+ rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
17
+ nil
14
18
  end
15
19
 
16
20
  def includes_path
@@ -27,18 +31,15 @@ module Bunto
27
31
 
28
32
  def configure_sass
29
33
  return unless sass_path
30
- require 'sass'
34
+ require "sass"
31
35
  Sass.load_paths << sass_path
32
36
  end
33
37
 
34
38
  private
35
39
 
36
40
  def path_for(folder)
37
- resolved_dir = realpath_for(folder)
38
- return unless resolved_dir
39
-
40
- path = Bunto.sanitized_path(root, resolved_dir)
41
- path if Dir.exists?(path)
41
+ path = realpath_for(folder)
42
+ path if path && File.directory?(path)
42
43
  end
43
44
 
44
45
  def realpath_for(folder)
@@ -50,7 +51,8 @@ module Bunto
50
51
  def gemspec
51
52
  @gemspec ||= Gem::Specification.find_by_name(name)
52
53
  rescue Gem::LoadError
53
- raise Bunto::Errors::MissingDependencyException, "The #{name} theme could not be found."
54
+ raise Bunto::Errors::MissingDependencyException,
55
+ "The #{name} theme could not be found."
54
56
  end
55
57
  end
56
58
  end
@@ -0,0 +1,117 @@
1
+ class Bunto::ThemeBuilder
2
+ SCAFFOLD_DIRECTORIES = %w(
3
+ _layouts _includes _sass
4
+ ).freeze
5
+
6
+ attr_reader :name, :path, :code_of_conduct
7
+
8
+ def initialize(theme_name, opts)
9
+ @name = theme_name.to_s.tr(" ", "_").gsub(%r!_+!, "_")
10
+ @path = Pathname.new(File.expand_path(name, Dir.pwd))
11
+ @code_of_conduct = !!opts["code_of_conduct"]
12
+ end
13
+
14
+ def create!
15
+ create_directories
16
+ create_starter_files
17
+ create_gemspec
18
+ create_accessories
19
+ initialize_git_repo
20
+ end
21
+
22
+ private
23
+
24
+ def root
25
+ @root ||= Pathname.new(File.expand_path("../", __dir__))
26
+ end
27
+
28
+ def template_file(filename)
29
+ [
30
+ root.join("theme_template", "#{filename}.erb"),
31
+ root.join("theme_template", filename.to_s)
32
+ ].find(&:exist?)
33
+ end
34
+
35
+ def template(filename)
36
+ erb.render(template_file(filename).read)
37
+ end
38
+
39
+ def erb
40
+ @erb ||= ERBRenderer.new(self)
41
+ end
42
+
43
+ def mkdir_p(directories)
44
+ Array(directories).each do |directory|
45
+ full_path = path.join(directory)
46
+ Bunto.logger.info "create", full_path.to_s
47
+ FileUtils.mkdir_p(full_path)
48
+ end
49
+ end
50
+
51
+ def write_file(filename, contents)
52
+ full_path = path.join(filename)
53
+ Bunto.logger.info "create", full_path.to_s
54
+ File.write(full_path, contents)
55
+ end
56
+
57
+ def create_directories
58
+ mkdir_p(SCAFFOLD_DIRECTORIES)
59
+ end
60
+
61
+ def create_starter_files
62
+ %w(page post default).each do |layout|
63
+ write_file("_layouts/#{layout}.html", template("_layouts/#{layout}.html"))
64
+ end
65
+ end
66
+
67
+ def create_gemspec
68
+ write_file("Gemfile", template("Gemfile"))
69
+ write_file("#{name}.gemspec", template("theme.gemspec"))
70
+ end
71
+
72
+ def create_accessories
73
+ accessories = %w(README.md LICENSE.txt)
74
+ accessories << "CODE_OF_CONDUCT.md" if code_of_conduct
75
+ accessories.each do |filename|
76
+ write_file(filename, template(filename))
77
+ end
78
+ end
79
+
80
+ def initialize_git_repo
81
+ Bunto.logger.info "initialize", path.join(".git").to_s
82
+ Dir.chdir(path.to_s) { `git init` }
83
+ write_file(".gitignore", template("gitignore"))
84
+ end
85
+
86
+ def user_name
87
+ @user_name ||= `git config user.name`.chomp
88
+ end
89
+
90
+ def user_email
91
+ @user_email ||= `git config user.email`.chomp
92
+ end
93
+
94
+ class ERBRenderer
95
+ extend Forwardable
96
+
97
+ def_delegator :@theme_builder, :name, :theme_name
98
+ def_delegator :@theme_builder, :user_name, :user_name
99
+ def_delegator :@theme_builder, :user_email, :user_email
100
+
101
+ def initialize(theme_builder)
102
+ @theme_builder = theme_builder
103
+ end
104
+
105
+ def bunto_version_with_minor
106
+ Bunto::VERSION.split(".").take(2).join(".")
107
+ end
108
+
109
+ def theme_directories
110
+ SCAFFOLD_DIRECTORIES
111
+ end
112
+
113
+ def render(contents)
114
+ ERB.new(contents).result binding
115
+ end
116
+ end
117
+ end