bunto 3.0.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
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