bridgetown-core 0.19.2 → 0.21.0.beta3

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/bridgetown-core.gemspec +1 -2
  3. data/lib/bridgetown-core.rb +37 -29
  4. data/lib/bridgetown-core/cleaner.rb +9 -3
  5. data/lib/bridgetown-core/collection.rb +177 -78
  6. data/lib/bridgetown-core/commands/base.rb +9 -0
  7. data/lib/bridgetown-core/commands/build.rb +0 -11
  8. data/lib/bridgetown-core/commands/concerns/git_helpers.rb +20 -0
  9. data/lib/bridgetown-core/commands/configure.rb +8 -3
  10. data/lib/bridgetown-core/commands/console.rb +4 -0
  11. data/lib/bridgetown-core/commands/doctor.rb +1 -19
  12. data/lib/bridgetown-core/commands/new.rb +2 -2
  13. data/lib/bridgetown-core/commands/plugins.rb +14 -13
  14. data/lib/bridgetown-core/commands/serve.rb +0 -14
  15. data/lib/bridgetown-core/component.rb +178 -0
  16. data/lib/bridgetown-core/concerns/data_accessible.rb +1 -0
  17. data/lib/bridgetown-core/concerns/front_matter_importer.rb +52 -0
  18. data/lib/bridgetown-core/concerns/site/configurable.rb +10 -10
  19. data/lib/bridgetown-core/concerns/site/content.rb +56 -15
  20. data/lib/bridgetown-core/concerns/site/localizable.rb +3 -5
  21. data/lib/bridgetown-core/concerns/site/processable.rb +6 -4
  22. data/lib/bridgetown-core/concerns/site/renderable.rb +26 -0
  23. data/lib/bridgetown-core/concerns/site/writable.rb +12 -2
  24. data/lib/bridgetown-core/concerns/validatable.rb +2 -5
  25. data/lib/bridgetown-core/configuration.rb +51 -30
  26. data/lib/bridgetown-core/configurations/bt-postcss.rb +1 -3
  27. data/lib/bridgetown-core/configurations/netlify.rb +1 -0
  28. data/lib/bridgetown-core/configurations/tailwindcss.rb +1 -3
  29. data/lib/bridgetown-core/converter.rb +23 -0
  30. data/lib/bridgetown-core/converters/erb_templates.rb +51 -35
  31. data/lib/bridgetown-core/converters/identity.rb +0 -9
  32. data/lib/bridgetown-core/converters/liquid_templates.rb +1 -1
  33. data/lib/bridgetown-core/converters/markdown.rb +14 -4
  34. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +5 -38
  35. data/lib/bridgetown-core/converters/ruby_templates.rb +17 -0
  36. data/lib/bridgetown-core/converters/smartypants.rb +3 -1
  37. data/lib/bridgetown-core/core_ext/psych.rb +19 -0
  38. data/lib/bridgetown-core/current.rb +10 -0
  39. data/lib/bridgetown-core/document.rb +9 -16
  40. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  41. data/lib/bridgetown-core/drops/page_drop.rb +4 -0
  42. data/lib/bridgetown-core/drops/relations_drop.rb +23 -0
  43. data/lib/bridgetown-core/drops/resource_drop.rb +83 -0
  44. data/lib/bridgetown-core/drops/site_drop.rb +33 -8
  45. data/lib/bridgetown-core/drops/unified_payload_drop.rb +5 -0
  46. data/lib/bridgetown-core/entry_filter.rb +17 -28
  47. data/lib/bridgetown-core/errors.rb +0 -2
  48. data/lib/bridgetown-core/filters.rb +3 -26
  49. data/lib/bridgetown-core/filters/from_liquid.rb +23 -0
  50. data/lib/bridgetown-core/filters/url_filters.rb +12 -0
  51. data/lib/bridgetown-core/frontmatter_defaults.rb +1 -1
  52. data/lib/bridgetown-core/generators/prototype_generator.rb +37 -19
  53. data/lib/bridgetown-core/helpers.rb +48 -9
  54. data/lib/bridgetown-core/layout.rb +28 -13
  55. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -0
  56. data/lib/bridgetown-core/liquid_renderer/table.rb +1 -0
  57. data/lib/bridgetown-core/model/base.rb +138 -0
  58. data/lib/bridgetown-core/model/builder_origin.rb +40 -0
  59. data/lib/bridgetown-core/model/origin.rb +38 -0
  60. data/lib/bridgetown-core/model/repo_origin.rb +126 -0
  61. data/lib/bridgetown-core/page.rb +11 -2
  62. data/lib/bridgetown-core/plugin_manager.rb +1 -3
  63. data/lib/bridgetown-core/publisher.rb +8 -2
  64. data/lib/bridgetown-core/reader.rb +37 -22
  65. data/lib/bridgetown-core/readers/data_reader.rb +5 -5
  66. data/lib/bridgetown-core/readers/defaults_reader.rb +1 -1
  67. data/lib/bridgetown-core/readers/page_reader.rb +1 -0
  68. data/lib/bridgetown-core/readers/post_reader.rb +5 -4
  69. data/lib/bridgetown-core/regenerator.rb +9 -2
  70. data/lib/bridgetown-core/related_posts.rb +9 -6
  71. data/lib/bridgetown-core/renderer.rb +6 -13
  72. data/lib/bridgetown-core/resource/base.rb +313 -0
  73. data/lib/bridgetown-core/resource/destination.rb +49 -0
  74. data/lib/bridgetown-core/resource/permalink_processor.rb +179 -0
  75. data/lib/bridgetown-core/resource/relations.rb +132 -0
  76. data/lib/bridgetown-core/resource/taxonomy_term.rb +34 -0
  77. data/lib/bridgetown-core/resource/taxonomy_type.rb +56 -0
  78. data/lib/bridgetown-core/resource/transformer.rb +177 -0
  79. data/lib/bridgetown-core/ruby_template_view.rb +11 -11
  80. data/lib/bridgetown-core/site.rb +13 -6
  81. data/lib/bridgetown-core/static_file.rb +33 -10
  82. data/lib/bridgetown-core/tags/highlight.rb +2 -15
  83. data/lib/bridgetown-core/tags/include.rb +1 -1
  84. data/lib/bridgetown-core/tags/post_url.rb +2 -2
  85. data/lib/bridgetown-core/url.rb +1 -0
  86. data/lib/bridgetown-core/utils.rb +49 -43
  87. data/lib/bridgetown-core/utils/require_gems.rb +60 -0
  88. data/lib/bridgetown-core/utils/ruby_exec.rb +6 -9
  89. data/lib/bridgetown-core/utils/ruby_front_matter.rb +39 -0
  90. data/lib/bridgetown-core/version.rb +2 -2
  91. data/lib/bridgetown-core/watcher.rb +1 -1
  92. data/lib/bridgetown-core/yaml_parser.rb +22 -0
  93. data/lib/site_template/package.json.erb +2 -2
  94. data/lib/site_template/plugins/site_builder.rb +1 -1
  95. data/lib/site_template/src/_posts/0000-00-00-welcome-to-bridgetown.md.erb +1 -1
  96. data/lib/site_template/webpack.config.js.erb +26 -6
  97. metadata +39 -40
  98. data/lib/bridgetown-core/external.rb +0 -58
  99. data/lib/bridgetown-core/page_without_a_file.rb +0 -17
  100. data/lib/bridgetown-core/path_manager.rb +0 -31
  101. data/lib/bridgetown-core/readers/collection_reader.rb +0 -23
  102. data/lib/bridgetown-core/readers/static_file_reader.rb +0 -25
  103. data/lib/bridgetown-core/utils/exec.rb +0 -26
  104. data/lib/bridgetown-core/utils/internet.rb +0 -37
  105. data/lib/bridgetown-core/utils/platforms.rb +0 -80
  106. data/lib/bridgetown-core/utils/thread_event.rb +0 -31
  107. data/lib/bridgetown-core/utils/win_tz.rb +0 -75
@@ -4,9 +4,7 @@
4
4
 
5
5
  TEMPLATE_PATH = File.expand_path("./bt-postcss", __dir__)
6
6
 
7
- begin
8
- find_in_source_paths("postcss.config.js")
9
- rescue Thor::Error
7
+ unless File.exist?("postcss.config.js")
10
8
  error_message = "#{"postcss.config.js".bold} not found. Please configure postcss in your project."
11
9
 
12
10
  @logger.error "\nError:".red, "🚨 #{error_message}"
@@ -4,3 +4,4 @@ TEMPLATE_PATH = File.expand_path("./netlify", __dir__)
4
4
 
5
5
  copy_file "#{TEMPLATE_PATH}/netlify.toml", "netlify.toml"
6
6
  copy_file "#{TEMPLATE_PATH}/netlify.sh", "bin/netlify.sh"
7
+ `chmod a+x ./bin/netlify.sh`
@@ -4,9 +4,7 @@
4
4
 
5
5
  TEMPLATE_PATH = File.expand_path("./tailwindcss", __dir__)
6
6
 
7
- begin
8
- find_in_source_paths("postcss.config.js")
9
- rescue Thor::Error
7
+ unless File.exist?("postcss.config.js")
10
8
  error_message = "#{"postcss.config.js".bold} not found. Please configure postcss in your project."
11
9
 
12
10
  @logger.error "\nError:".red, "🚨 #{error_message}"
@@ -23,6 +23,16 @@ module Bridgetown
23
23
  @config = config
24
24
  end
25
25
 
26
+ # Logic to do the content conversion.
27
+ #
28
+ # @param content [String] content of file (without front matter).
29
+ # @param convertible [Bridgetown::Document, Bridgetown::Layout, Bridgetown::Resource::Base]
30
+ #
31
+ # @return [String] the converted content.
32
+ def convert(content, convertible = nil) # rubocop:disable Lint/UnusedMethodArgument
33
+ content
34
+ end
35
+
26
36
  # Does the given extension match this converter's list of acceptable extensions?
27
37
  #
28
38
  # @param [String] ext
@@ -42,5 +52,18 @@ module Bridgetown
42
52
  def output_ext(_ext)
43
53
  ".html"
44
54
  end
55
+
56
+ def line_start(convertible)
57
+ if convertible.is_a?(Bridgetown::Resource::Base) &&
58
+ convertible.model.origin.respond_to?(:front_matter_line_count)
59
+ convertible.model.origin.front_matter_line_count + 4
60
+ else
61
+ 1
62
+ end
63
+ end
64
+
65
+ def inspect
66
+ "#<#{self.class}#{self.class.extname_list ? " #{self.class.extname_list.join(", ")}" : nil}>"
67
+ end
45
68
  end
46
69
  end
@@ -3,14 +3,26 @@
3
3
  require "tilt/erubi"
4
4
 
5
5
  module Bridgetown
6
- class ERBBuffer < String
7
- def concat_to_s(input)
8
- concat input.to_s
6
+ class OutputBuffer < ActiveSupport::SafeBuffer
7
+ def initialize(*)
8
+ super
9
+ encode!
9
10
  end
10
11
 
11
- alias_method :safe_append=, :concat_to_s
12
- alias_method :append=, :concat_to_s
13
- alias_method :safe_expr_append=, :concat_to_s
12
+ def <<(value)
13
+ return self if value.nil?
14
+
15
+ super(value.to_s)
16
+ end
17
+ alias_method :append=, :<<
18
+
19
+ def safe_expr_append=(val)
20
+ return self if val.nil? # rubocop:disable Lint/ReturnInVoidContext
21
+
22
+ safe_concat val.to_s
23
+ end
24
+
25
+ alias_method :safe_append=, :safe_concat
14
26
  end
15
27
 
16
28
  class ERBEngine < Erubi::Engine
@@ -22,24 +34,47 @@ module Bridgetown
22
34
  @src << ";" unless code[Erubi::RANGE_LAST] == "\n"
23
35
  end
24
36
 
37
+ def add_text(text)
38
+ return if text.empty?
39
+
40
+ src << bufvar << ".safe_append='"
41
+ src << text.gsub(%r{['\\]}, '\\\\\&')
42
+ src << "'.freeze;"
43
+ end
44
+
25
45
  # pulled from Rails' ActionView
26
46
  BLOCK_EXPR = %r!\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z!.freeze
27
47
 
28
48
  def add_expression(indicator, code)
49
+ src << bufvar << if (indicator == "==") || @escape
50
+ ".safe_expr_append="
51
+ else
52
+ ".append="
53
+ end
54
+
29
55
  if BLOCK_EXPR.match?(code)
30
- src << "#{@bufvar}.append= " << code
56
+ src << " " << code
31
57
  else
32
- super
58
+ src << "(" << code << ");"
33
59
  end
34
60
  end
61
+ end
35
62
 
36
- # Don't allow == to output escaped strings, as that's the opposite of Rails
37
- def add_expression_result_escaped(code)
38
- add_expression_result(code)
63
+ module ERBCapture
64
+ def capture(*args)
65
+ previous_buffer_state = @_erbout
66
+ @_erbout = OutputBuffer.new
67
+ result = yield(*args)
68
+ result = @_erbout.presence || result
69
+ @_erbout = previous_buffer_state
70
+
71
+ result.is_a?(String) ? ERB::Util.h(result) : result
39
72
  end
40
73
  end
41
74
 
42
75
  class ERBView < RubyTemplateView
76
+ include ERBCapture
77
+
43
78
  def h(input)
44
79
  Erubi.h(input)
45
80
  end
@@ -55,30 +90,10 @@ module Bridgetown
55
90
  Tilt::ErubiTemplate.new(
56
91
  site.in_source_dir(site.config[:partials_dir], "#{partial_name}.erb"),
57
92
  outvar: "@_erbout",
58
- bufval: "Bridgetown::ERBBuffer.new",
93
+ bufval: "Bridgetown::OutputBuffer.new",
59
94
  engine_class: ERBEngine
60
95
  ).render(self, options)
61
96
  end
62
-
63
- def markdownify(input = nil, &block)
64
- content = Bridgetown::Utils.reindent_for_markdown(
65
- block.nil? ? input.to_s : capture(&block)
66
- )
67
- converter = site.find_converter_instance(Bridgetown::Converters::Markdown)
68
- result = converter.convert(content).strip
69
- result.respond_to?(:html_safe) ? result.html_safe : result
70
- end
71
-
72
- def capture(*args, &block)
73
- return capture_in_view_component(*args, &block) if @in_view_component
74
-
75
- previous_buffer_state = @_erbout
76
- @_erbout = ERBBuffer.new
77
- result = yield(*args)
78
- @_erbout = previous_buffer_state
79
-
80
- result.respond_to?(:html_safe) ? result.html_safe : result
81
- end
82
97
  end
83
98
 
84
99
  module Converters
@@ -89,7 +104,7 @@ module Bridgetown
89
104
  # Logic to do the ERB content conversion.
90
105
  #
91
106
  # @param content [String] Content of the file (without front matter).
92
- # @params convertible [Bridgetown::Page, Bridgetown::Document, Bridgetown::Layout]
107
+ # @param convertible [Bridgetown::Page, Bridgetown::Document, Bridgetown::Layout]
93
108
  # The instantiated object which is processing the file.
94
109
  #
95
110
  # @return [String] The converted content.
@@ -100,14 +115,15 @@ module Bridgetown
100
115
 
101
116
  erb_renderer = Tilt::ErubiTemplate.new(
102
117
  convertible.relative_path,
118
+ line_start(convertible),
103
119
  outvar: "@_erbout",
104
- bufval: "Bridgetown::ERBBuffer.new",
120
+ bufval: "Bridgetown::OutputBuffer.new",
105
121
  engine_class: ERBEngine
106
122
  ) { content }
107
123
 
108
124
  if convertible.is_a?(Bridgetown::Layout)
109
125
  erb_renderer.render(erb_view) do
110
- convertible.current_document_output
126
+ convertible.current_document_output.html_safe
111
127
  end
112
128
  else
113
129
  erb_renderer.render(erb_view)
@@ -25,15 +25,6 @@ module Bridgetown
25
25
  def output_ext(ext)
26
26
  ext
27
27
  end
28
-
29
- # Logic to do the content conversion.
30
- #
31
- # content - String content of file (without front matter).
32
- #
33
- # Returns a String of the converted content.
34
- def convert(content)
35
- content
36
- end
37
28
  end
38
29
  end
39
30
  end
@@ -18,7 +18,7 @@ module Bridgetown
18
18
  # Logic to do the Liquid content conversion.
19
19
  #
20
20
  # @param content [String] Content of the file (without front matter).
21
- # @params convertible [Bridgetown::Page, Bridgetown::Document, Bridgetown::Layout]
21
+ # @param convertible [Bridgetown::Page, Bridgetown::Document, Bridgetown::Layout]
22
22
  # The instantiated object which is processing the file.
23
23
  #
24
24
  # @return [String] The converted content.
@@ -22,7 +22,9 @@ module Bridgetown
22
22
  raise Errors::FatalException, "Invalid Markdown processor given: #{@config["markdown"]}"
23
23
  end
24
24
 
25
- @cache = Bridgetown::Cache.new("Bridgetown::Converters::Markdown")
25
+ unless @config.cache_markdown == false || @config.kramdown.input == "GFMExtractions"
26
+ @cache = Bridgetown::Cache.new("Bridgetown::Converters::Markdown")
27
+ end
26
28
  @setup = true
27
29
  end
28
30
 
@@ -59,10 +61,18 @@ module Bridgetown
59
61
  # content - String content of file (without front matter).
60
62
  #
61
63
  # Returns a String of the converted content.
62
- def convert(content)
64
+ def convert(content, convertible = nil)
63
65
  setup
64
- @cache.getset(content) do
65
- @parser.convert(content)
66
+ if @cache
67
+ @cache.getset(content) do
68
+ @parser.convert(content)
69
+ end
70
+ else
71
+ output = @parser.convert(content)
72
+ if convertible && @parser.respond_to?(:extractions)
73
+ convertible.data.markdown_extractions = @parser.extractions
74
+ end
75
+ output
66
76
  end
67
77
  end
68
78
 
@@ -69,22 +69,14 @@ module Bridgetown
69
69
  module Converters
70
70
  class Markdown
71
71
  class KramdownParser
72
+ attr_reader :extractions
73
+
72
74
  def initialize(config)
73
- @main_fallback_highlighter = config["highlighter"] || "rouge"
74
75
  @config = config["kramdown"] || {}
75
- @highlighter = nil
76
- setup
77
- load_dependencies
78
- end
79
-
80
- # Setup and normalize the configuration:
81
- # * Create Kramdown if it doesn't exist.
82
- # * Set syntax_highlighter
83
- # * Make sure `syntax_highlighter_opts` exists.
84
- def setup
85
- @config["syntax_highlighter"] ||= highlighter
76
+ @config["syntax_highlighter"] ||= config["highlighter"] || "rouge"
86
77
  @config["syntax_highlighter_opts"] ||= {}
87
78
  @config["syntax_highlighter_opts"]["guess_lang"] = @config["guess_lang"]
79
+ require "kramdown-parser-gfm" if @config["input"] == "GFM"
88
80
  end
89
81
 
90
82
  def convert(content)
@@ -95,34 +87,9 @@ module Bridgetown
95
87
  Bridgetown.logger.warn "Kramdown warning:", warning
96
88
  end
97
89
  end
90
+ @extractions = document.root.options[:extractions] # could be nil
98
91
  html_output
99
92
  end
100
-
101
- private
102
-
103
- def load_dependencies
104
- require "kramdown-parser-gfm" if @config["input"] == "GFM"
105
-
106
- # `mathjax` emgine is bundled within kramdown-2.x and will be handled by
107
- # kramdown itself.
108
- if (math_engine = @config["math_engine"]) && math_engine != "mathjax"
109
- Bridgetown::External.require_with_graceful_fail("kramdown-math-#{math_engine}")
110
- end
111
- end
112
-
113
- # config[kramdown][syntax_higlighter] >
114
- # config[highlighter]
115
- def highlighter
116
- return @highlighter if @highlighter
117
-
118
- if @config["syntax_highlighter"]
119
- return @highlighter = @config[
120
- "syntax_highlighter"
121
- ]
122
- end
123
-
124
- @highlighter = @main_fallback_highlighter
125
- end
126
93
  end
127
94
  end
128
95
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Converters
5
+ class RubyTemplates < Converter
6
+ priority :highest
7
+ input :rb
8
+
9
+ def convert(content, convertible)
10
+ erb_view = Bridgetown::ERBView.new(convertible)
11
+ erb_view.instance_eval(
12
+ content, convertible.relative_path.to_s, line_start(convertible)
13
+ ).to_s
14
+ end
15
+ end
16
+ end
17
+ end
@@ -25,7 +25,9 @@ module Bridgetown
25
25
  priority :low
26
26
 
27
27
  def initialize(config)
28
- Bridgetown::External.require_with_graceful_fail "kramdown" unless defined?(Kramdown)
28
+ unless defined?(Kramdown)
29
+ Bridgetown::Utils::RequireGems.require_with_graceful_fail "kramdown"
30
+ end
29
31
  @config = config["kramdown"].dup || {}
30
32
  @config[:input] = :SmartyPants
31
33
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module CoreExt
5
+ module Psych
6
+ module SafeLoadFile
7
+ def safe_load_file(filename, **kwargs)
8
+ File.open(filename, "r:bom|utf-8") do |f|
9
+ if RUBY_VERSION.start_with?("2.5")
10
+ safe_load f, kwargs[:permitted_classes], [], false, filename
11
+ else
12
+ safe_load f, filename: filename, **kwargs
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class Current < ActiveSupport::CurrentAttributes
5
+ # @!method self.site
6
+ # @return [Bridgetown::Site]
7
+
8
+ attribute :site
9
+ end
10
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bridgetown
4
+ # TODO: to be retired once the Resource engine is made official
4
5
  class Document
5
6
  extend Forwardable
6
7
  include DataAccessible
@@ -52,7 +53,7 @@ module Bridgetown
52
53
  @has_yaml_header = nil
53
54
 
54
55
  data.default_proc = proc do |_, key|
55
- site.frontmatter_defaults.find(relative_path, type, key)
56
+ site.frontmatter_defaults.find(relative_path, type, key.to_s)
56
57
  end
57
58
 
58
59
  trigger_hooks(:post_init)
@@ -81,7 +82,7 @@ module Bridgetown
81
82
  #
82
83
  # Return document date string.
83
84
  def date
84
- data["date"] ||= site.time
85
+ data["date"] ||= site.time # TODO: this doesn't reflect documented behavior
85
86
  end
86
87
 
87
88
  # Return document file modification time in the form of a Time object.
@@ -223,7 +224,7 @@ module Bridgetown
223
224
  Bridgetown.logger.debug "Reading:", relative_path
224
225
 
225
226
  if yaml_file?
226
- @data = SafeYAML.load_file(path)
227
+ @data = YAMLParser.load_file(path)
227
228
  else
228
229
  begin
229
230
  merge_defaults
@@ -357,7 +358,7 @@ module Bridgetown
357
358
  self.content = File.read(path, **Utils.merged_file_read_opts(site, opts))
358
359
  if content =~ YAML_FRONT_MATTER_REGEXP
359
360
  self.content = $POSTMATCH
360
- data_file = SafeYAML.load(Regexp.last_match(1))
361
+ data_file = YAMLParser.load(Regexp.last_match(1))
361
362
  merge_data!(data_file, source: "YAML front matter") if data_file
362
363
  end
363
364
  end
@@ -402,21 +403,13 @@ module Bridgetown
402
403
  end
403
404
 
404
405
  def populate_categories
405
- categories = Array(data["categories"]) + Utils.pluralized_array_from_hash(
406
- data, "category", "categories"
407
- )
408
- categories.map!(&:to_s)
409
- categories.flatten!
410
- categories.uniq!
411
-
412
- merge_data!({ "categories" => categories })
406
+ data.categories = Utils.pluralized_array_from_hash(
407
+ data, :category, :categories
408
+ ).map(&:to_s)
413
409
  end
414
410
 
415
411
  def populate_tags
416
- tags = Utils.pluralized_array_from_hash(data, "tag", "tags")
417
- tags.flatten!
418
-
419
- merge_data!({ "tags" => tags })
412
+ data.tags = Utils.pluralized_array_from_hash(data, :tag, :tags)
420
413
  end
421
414
 
422
415
  def determine_locale