jekyll 3.2.0.pre.beta1 → 3.2.0.pre.beta2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of jekyll might be problematic. Click here for more details.

Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -95
  3. data/README.markdown +6 -4
  4. data/{bin → exe}/jekyll +18 -14
  5. data/lib/jekyll.rb +79 -76
  6. data/lib/jekyll/collection.rb +32 -16
  7. data/lib/jekyll/command.rb +17 -13
  8. data/lib/jekyll/commands/build.rb +7 -2
  9. data/lib/jekyll/commands/doctor.rb +3 -1
  10. data/lib/jekyll/commands/new.rb +3 -0
  11. data/lib/jekyll/commands/new_theme.rb +7 -4
  12. data/lib/jekyll/commands/serve.rb +2 -0
  13. data/lib/jekyll/commands/serve/servlet.rb +2 -2
  14. data/lib/jekyll/configuration.rb +187 -125
  15. data/lib/jekyll/converters/markdown.rb +19 -9
  16. data/lib/jekyll/converters/markdown/kramdown_parser.rb +12 -5
  17. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +4 -4
  18. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +90 -84
  19. data/lib/jekyll/convertible.rb +34 -21
  20. data/lib/jekyll/deprecator.rb +11 -6
  21. data/lib/jekyll/document.rb +52 -50
  22. data/lib/jekyll/drops/document_drop.rb +40 -5
  23. data/lib/jekyll/drops/drop.rb +49 -10
  24. data/lib/jekyll/drops/excerpt_drop.rb +15 -0
  25. data/lib/jekyll/drops/jekyll_drop.rb +12 -0
  26. data/lib/jekyll/drops/site_drop.rb +4 -2
  27. data/lib/jekyll/drops/url_drop.rb +4 -4
  28. data/lib/jekyll/entry_filter.rb +9 -6
  29. data/lib/jekyll/errors.rb +4 -3
  30. data/lib/jekyll/excerpt.rb +4 -6
  31. data/lib/jekyll/external.rb +4 -4
  32. data/lib/jekyll/filters.rb +67 -34
  33. data/lib/jekyll/frontmatter_defaults.rb +45 -38
  34. data/lib/jekyll/hooks.rb +21 -21
  35. data/lib/jekyll/layout.rb +3 -1
  36. data/lib/jekyll/liquid_renderer.rb +7 -3
  37. data/lib/jekyll/liquid_renderer/file.rb +1 -1
  38. data/lib/jekyll/liquid_renderer/table.rb +11 -11
  39. data/lib/jekyll/log_adapter.rb +2 -2
  40. data/lib/jekyll/page.rb +10 -10
  41. data/lib/jekyll/plugin.rb +5 -5
  42. data/lib/jekyll/plugin_manager.rb +12 -8
  43. data/lib/jekyll/publisher.rb +1 -1
  44. data/lib/jekyll/reader.rb +11 -7
  45. data/lib/jekyll/readers/data_reader.rb +9 -9
  46. data/lib/jekyll/readers/layout_reader.rb +7 -7
  47. data/lib/jekyll/readers/page_reader.rb +3 -1
  48. data/lib/jekyll/readers/post_reader.rb +9 -10
  49. data/lib/jekyll/readers/static_file_reader.rb +3 -1
  50. data/lib/jekyll/regenerator.rb +50 -28
  51. data/lib/jekyll/related_posts.rb +1 -1
  52. data/lib/jekyll/renderer.rb +29 -20
  53. data/lib/jekyll/site.rb +92 -50
  54. data/lib/jekyll/static_file.rb +33 -26
  55. data/lib/jekyll/stevenson.rb +6 -5
  56. data/lib/jekyll/tags/highlight.rb +50 -35
  57. data/lib/jekyll/tags/include.rb +42 -31
  58. data/lib/jekyll/tags/link.rb +11 -4
  59. data/lib/jekyll/tags/post_url.rb +8 -7
  60. data/lib/jekyll/theme.rb +4 -3
  61. data/lib/jekyll/theme_builder.rb +18 -6
  62. data/lib/jekyll/url.rb +21 -14
  63. data/lib/jekyll/utils.rb +57 -28
  64. data/lib/jekyll/utils/ansi.rb +9 -9
  65. data/lib/jekyll/utils/platforms.rb +2 -2
  66. data/lib/jekyll/version.rb +1 -1
  67. data/lib/site_template/_config.yml +2 -0
  68. data/lib/site_template/css/main.scss +3 -17
  69. data/lib/theme_template/_layouts/default.html +1 -0
  70. data/lib/theme_template/_layouts/page.html +5 -0
  71. data/lib/theme_template/_layouts/post.html +5 -0
  72. data/lib/theme_template/example/_post.md +1 -2
  73. data/lib/theme_template/example/index.html +2 -2
  74. data/lib/theme_template/gitignore.erb +4 -0
  75. data/lib/theme_template/theme.gemspec.erb +2 -6
  76. metadata +10 -18
  77. data/lib/site_template/_includes/footer.html +0 -38
  78. data/lib/site_template/_includes/head.html +0 -12
  79. data/lib/site_template/_includes/header.html +0 -27
  80. data/lib/site_template/_includes/icon-github.html +0 -1
  81. data/lib/site_template/_includes/icon-github.svg +0 -1
  82. data/lib/site_template/_includes/icon-twitter.html +0 -1
  83. data/lib/site_template/_includes/icon-twitter.svg +0 -1
  84. data/lib/site_template/_layouts/default.html +0 -20
  85. data/lib/site_template/_layouts/page.html +0 -14
  86. data/lib/site_template/_layouts/post.html +0 -15
  87. data/lib/site_template/_sass/_base.scss +0 -200
  88. data/lib/site_template/_sass/_layout.scss +0 -242
  89. data/lib/site_template/_sass/_syntax-highlighting.scss +0 -71
@@ -9,20 +9,24 @@ module Jekyll
9
9
  'serve' subcommand."
10
10
  arg_is_present? args, "--no-server", "To build Jekyll without launching a server, \
11
11
  use the 'build' subcommand."
12
- arg_is_present? args, "--auto", "The switch '--auto' has been replaced with '--watch'."
12
+ arg_is_present? args, "--auto", "The switch '--auto' has been replaced with \
13
+ '--watch'."
13
14
  arg_is_present? args, "--no-auto", "To disable auto-replication, simply leave off \
14
15
  the '--watch' switch."
15
16
  arg_is_present? args, "--pygments", "The 'pygments'settings has been removed in \
16
17
  favour of 'highlighter'."
17
- arg_is_present? args, "--paginate", "The 'paginate' setting can only be set in your \
18
+ arg_is_present? args, "--paginate", "The 'paginate' setting can only be set in \
19
+ your config files."
20
+ arg_is_present? args, "--url", "The 'url' setting can only be set in your \
18
21
  config files."
19
- arg_is_present? args, "--url", "The 'url' setting can only be set in your config files."
20
22
  no_subcommand(args)
21
23
  end
22
24
 
23
25
  def no_subcommand(args)
24
- if args.size > 0 && args.first =~ /^--/ && !%w(--help --version).include?(args.first)
25
- deprecation_message "Jekyll now uses subcommands instead of just switches. Run `jekyll help` to find out more."
26
+ unless args.empty? ||
27
+ args.first !~ %r(!/^--/!) || %w(--help --version).include?(args.first)
28
+ deprecation_message "Jekyll now uses subcommands instead of just switches. \
29
+ Run `jekyll help` to find out more."
26
30
  abort
27
31
  end
28
32
  end
@@ -39,7 +43,8 @@ module Jekyll
39
43
 
40
44
  def defaults_deprecate_type(old, current)
41
45
  Jekyll.logger.warn "Defaults:", "The '#{old}' type has become '#{current}'."
42
- Jekyll.logger.warn "Defaults:", "Please update your front-matter defaults to use 'type: #{current}'."
46
+ Jekyll.logger.warn "Defaults:", "Please update your front-matter defaults to use \
47
+ 'type: #{current}'."
43
48
  end
44
49
 
45
50
  end
@@ -7,9 +7,9 @@ module Jekyll
7
7
  attr_reader :path, :site, :extname, :collection
8
8
  attr_accessor :content, :output
9
9
 
10
- YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
11
- DATELESS_FILENAME_MATCHER = /^(.+\/)*(.*)(\.[^.]+)$/
12
- DATE_FILENAME_MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
10
+ YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
11
+ DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!
12
+ DATE_FILENAME_MATCHER = %r!^(?:.+/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$!
13
13
 
14
14
  # Create a new Document.
15
15
  #
@@ -51,16 +51,16 @@ module Jekyll
51
51
  #
52
52
  # Returns the merged data.
53
53
  def merge_data!(other, source: "YAML front matter")
54
- if other.key?('categories') && !other['categories'].nil?
55
- if other['categories'].is_a?(String)
56
- other['categories'] = other['categories'].split(" ").map(&:strip)
54
+ if other.key?("categories") && !other["categories"].nil?
55
+ if other["categories"].is_a?(String)
56
+ other["categories"] = other["categories"].split(" ").map(&:strip)
57
57
  end
58
- other['categories'] = (data['categories'] || []) | other['categories']
58
+ other["categories"] = (data["categories"] || []) | other["categories"]
59
59
  end
60
60
  Utils.deep_merge_hashes!(data, other)
61
- if data.key?('date') && !data['date'].is_a?(Time)
62
- data['date'] = Utils.parse_date(
63
- data['date'].to_s,
61
+ if data.key?("date") && !data["date"].is_a?(Time)
62
+ data["date"] = Utils.parse_date(
63
+ data["date"].to_s,
64
64
  "Document '#{relative_path}' does not have a valid date in the #{source}."
65
65
  )
66
66
  end
@@ -68,7 +68,7 @@ module Jekyll
68
68
  end
69
69
 
70
70
  def date
71
- data['date'] ||= (draft? ? source_file_mtime : site.time)
71
+ data["date"] ||= (draft? ? source_file_mtime : site.time)
72
72
  end
73
73
 
74
74
  def source_file_mtime
@@ -81,7 +81,8 @@ module Jekyll
81
81
  #
82
82
  # Returns whether the document is a draft.
83
83
  def draft?
84
- data['draft'] ||= relative_path.index(collection.relative_directory).nil? && collection.label == "posts"
84
+ data["draft"] ||= relative_path.index(collection.relative_directory).nil? &&
85
+ collection.label == "posts"
85
86
  end
86
87
 
87
88
  # The path to the document, relative to the site source.
@@ -89,7 +90,8 @@ module Jekyll
89
90
  # Returns a String path which represents the relative path
90
91
  # from the site source to this document
91
92
  def relative_path
92
- @relative_path ||= Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
93
+ @relative_path ||= Pathname.new(path)
94
+ .relative_path_from(Pathname.new(site.source)).to_s
93
95
  end
94
96
 
95
97
  # The output extension of the document.
@@ -103,7 +105,7 @@ module Jekyll
103
105
  #
104
106
  # Returns the basename without the file extname.
105
107
  def basename_without_ext
106
- @basename_without_ext ||= File.basename(path, '.*')
108
+ @basename_without_ext ||= File.basename(path, ".*")
107
109
  end
108
110
 
109
111
  # The base filename of the document.
@@ -156,7 +158,7 @@ module Jekyll
156
158
  #
157
159
  # Returns true if extname == .coffee, false otherwise.
158
160
  def coffeescript_file?
159
- '.coffee'.eql?(extname)
161
+ ".coffee" == extname
160
162
  end
161
163
 
162
164
  # Determine whether the file should be rendered with Liquid.
@@ -195,7 +197,7 @@ module Jekyll
195
197
  #
196
198
  # Returns the permalink or nil if no permalink was set in the data.
197
199
  def permalink
198
- data && data.is_a?(Hash) && data['permalink']
200
+ data && data.is_a?(Hash) && data["permalink"]
199
201
  end
200
202
 
201
203
  # The computed URL for the document. See `Jekyll::URL#to_s` for more details.
@@ -203,9 +205,9 @@ module Jekyll
203
205
  # Returns the computed URL for the document.
204
206
  def url
205
207
  @url = URL.new({
206
- :template => url_template,
208
+ :template => url_template,
207
209
  :placeholders => url_placeholders,
208
- :permalink => permalink
210
+ :permalink => permalink
209
211
  }).to_s
210
212
  end
211
213
 
@@ -237,18 +239,17 @@ module Jekyll
237
239
  def write(dest)
238
240
  path = destination(dest)
239
241
  FileUtils.mkdir_p(File.dirname(path))
240
- File.open(path, 'wb') do |f|
241
- f.write(output)
242
- end
242
+ File.write(path, output, :mode => "wb")
243
243
 
244
244
  trigger_hooks(:post_write)
245
245
  end
246
246
 
247
247
  # Whether the file is published or not, as indicated in YAML front-matter
248
248
  #
249
- # Returns true if the 'published' key is specified in the YAML front-matter and not `false`.
249
+ # Returns 'false' if the 'published' key is specified in the
250
+ # YAML front-matter and is 'false'. Otherwise returns 'true'.
250
251
  def published?
251
- !(data.key?('published') && data['published'] == false)
252
+ !(data.key?("published") && data["published"] == false)
252
253
  end
253
254
 
254
255
  # Read in the file and assign the content and data based on the file contents.
@@ -263,23 +264,24 @@ module Jekyll
263
264
  @data = SafeYAML.load_file(path)
264
265
  else
265
266
  begin
266
- defaults = @site.frontmatter_defaults.all(relative_path, collection.label.to_sym)
267
- merge_data!(defaults, source: "front matter defaults") unless defaults.empty?
267
+ defaults = @site.frontmatter_defaults.all(
268
+ relative_path,
269
+ collection.label.to_sym
270
+ )
271
+ merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?
268
272
 
269
273
  self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
270
274
  if content =~ YAML_FRONT_MATTER_REGEXP
271
275
  self.content = $POSTMATCH
272
276
  data_file = SafeYAML.load(Regexp.last_match(1))
273
- merge_data!(data_file, source: "YAML front matter") if data_file
277
+ merge_data!(data_file, :source => "YAML front matter") if data_file
274
278
  end
275
279
 
276
280
  post_read
277
281
  rescue SyntaxError => e
278
282
  Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{e.message}"
279
- rescue Exception => e
280
- if e.is_a? Jekyll::Errors::FatalException
281
- raise e
282
- end
283
+ rescue => e
284
+ raise e if e.is_a? Jekyll::Errors::FatalException
283
285
  Jekyll.logger.error "Error:", "could not read file #{path}: #{e.message}"
284
286
  end
285
287
  end
@@ -287,19 +289,19 @@ module Jekyll
287
289
 
288
290
  def post_read
289
291
  if relative_path =~ DATE_FILENAME_MATCHER
290
- date, slug, ext = $2, $3, $4
291
- if !data['date'] || data['date'].to_i == site.time.to_i
292
- merge_data!({"date" => date}, source: "filename")
292
+ date, slug, ext = Regexp.last_match.captures
293
+ if !data["date"] || data["date"].to_i == site.time.to_i
294
+ merge_data!({ "date" => date }, :source => "filename")
293
295
  end
294
296
  elsif relative_path =~ DATELESS_FILENAME_MATCHER
295
- slug, ext = $2, $3
297
+ slug, ext = Regexp.last_match.captures
296
298
  end
297
299
 
298
300
  # Try to ensure the user gets a title.
299
301
  data["title"] ||= Utils.titleize_slug(slug)
300
302
  # Only overwrite slug & ext if they aren't specified.
301
- data['slug'] ||= slug
302
- data['ext'] ||= ext
303
+ data["slug"] ||= slug
304
+ data["ext"] ||= ext
303
305
 
304
306
  populate_categories
305
307
  populate_tags
@@ -312,16 +314,19 @@ module Jekyll
312
314
  #
313
315
  # Returns nothing.
314
316
  def categories_from_path(special_dir)
315
- superdirs = relative_path.sub(/#{special_dir}(.*)/, '').split(File::SEPARATOR).reject do |c|
317
+ superdirs = relative_path.sub(%r!#{special_dir}(.*)!, "")
318
+ .split(File::SEPARATOR)
319
+ .reject do |c|
316
320
  c.empty? || c.eql?(special_dir) || c.eql?(basename)
317
321
  end
318
- merge_data!({ 'categories' => superdirs }, source: "file path")
322
+ merge_data!({ "categories" => superdirs }, :source => "file path")
319
323
  end
320
324
 
321
325
  def populate_categories
322
326
  merge_data!({
323
- 'categories' => (
324
- Array(data['categories']) + Utils.pluralized_array_from_hash(data, 'category', 'categories')
327
+ "categories" => (
328
+ Array(data["categories"]) +
329
+ Utils.pluralized_array_from_hash(data, "category", "categories")
325
330
  ).map(&:to_s).flatten.uniq
326
331
  })
327
332
  end
@@ -351,7 +356,7 @@ module Jekyll
351
356
  #
352
357
  # Returns the content of the document
353
358
  def to_s
354
- output || content || 'NO CONTENT'
359
+ output || content || "NO CONTENT"
355
360
  end
356
361
 
357
362
  # Compare this document against another document.
@@ -361,7 +366,7 @@ module Jekyll
361
366
  # equal or greater than the other doc's path. See String#<=> for more details.
362
367
  def <=>(other)
363
368
  return nil unless other.respond_to?(:data)
364
- cmp = data['date'] <=> other.data['date']
369
+ cmp = data["date"] <=> other.data["date"]
365
370
  cmp = path <=> other.path if cmp.nil? || cmp == 0
366
371
  cmp
367
372
  end
@@ -380,7 +385,7 @@ module Jekyll
380
385
  #
381
386
  # Returns the document excerpt_separator
382
387
  def excerpt_separator
383
- (data['excerpt_separator'] || site.config['excerpt_separator']).to_s
388
+ (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
384
389
  end
385
390
 
386
391
  # Whether to generate an excerpt
@@ -394,8 +399,6 @@ module Jekyll
394
399
  pos = collection.docs.index { |post| post.equal?(self) }
395
400
  if pos && pos < collection.docs.length - 1
396
401
  collection.docs[pos + 1]
397
- else
398
- nil
399
402
  end
400
403
  end
401
404
 
@@ -403,8 +406,6 @@ module Jekyll
403
406
  pos = collection.docs.index { |post| post.equal?(self) }
404
407
  if pos && pos > 0
405
408
  collection.docs[pos - 1]
406
- else
407
- nil
408
409
  end
409
410
  end
410
411
 
@@ -414,7 +415,7 @@ module Jekyll
414
415
  end
415
416
 
416
417
  def id
417
- @id ||= File.join(File.dirname(url), (data['slug'] || basename_without_ext).to_s)
418
+ @id ||= File.join(File.dirname(url), (data["slug"] || basename_without_ext).to_s)
418
419
  end
419
420
 
420
421
  # Calculate related posts.
@@ -433,8 +434,9 @@ module Jekyll
433
434
  # Override of method_missing to check in @data for the key.
434
435
  def method_missing(method, *args, &blck)
435
436
  if data.key?(method.to_s)
436
- Jekyll.logger.warn "Deprecation:", "Document##{method} is now a key in the #data hash."
437
- Jekyll.logger.warn "", "Called by #{caller.first}."
437
+ Jekyll::Deprecator.deprecation_message "Document##{method} is now a key "\
438
+ "in the #data hash."
439
+ Jekyll::Deprecator.deprecation_message "Called by #{caller.first}."
438
440
  data[method.to_s]
439
441
  else
440
442
  super
@@ -5,10 +5,12 @@ module Jekyll
5
5
  class DocumentDrop < Drop
6
6
  extend Forwardable
7
7
 
8
+ NESTED_OBJECT_FIELD_BLACKLIST = %w(
9
+ content output excerpt next previous
10
+ ).freeze
11
+
8
12
  mutable false
9
13
 
10
- def_delegator :@obj, :next_doc, :next
11
- def_delegator :@obj, :previous_doc, :previous
12
14
  def_delegator :@obj, :relative_path, :path
13
15
  def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url
14
16
 
@@ -17,16 +19,49 @@ module Jekyll
17
19
  end
18
20
 
19
21
  def excerpt
20
- fallback_data['excerpt'].to_s
22
+ fallback_data["excerpt"].to_s
21
23
  end
22
24
 
23
25
  def <=>(other)
24
26
  return nil unless other.is_a? DocumentDrop
25
- cmp = self['date'] <=> other['date']
26
- cmp = self['path'] <=> other['path'] if cmp.nil? || cmp == 0
27
+ cmp = self["date"] <=> other["date"]
28
+ cmp = self["path"] <=> other["path"] if cmp.nil? || cmp == 0
27
29
  cmp
28
30
  end
29
31
 
32
+ def previous
33
+ @obj.previous_doc.to_liquid
34
+ end
35
+
36
+ def next
37
+ @obj.next_doc.to_liquid
38
+ end
39
+
40
+ # Generate a Hash for use in generating JSON.
41
+ # This is useful if fields need to be cleared before the JSON can generate.
42
+ #
43
+ # state - the JSON::State object which determines the state of current processing.
44
+ #
45
+ # Returns a Hash ready for JSON generation.
46
+ def hash_for_json(state = nil)
47
+ to_h.tap do |hash|
48
+ if state && state.depth >= 2
49
+ hash["previous"] = collapse_document(hash["previous"]) if hash["previous"]
50
+ hash["next"] = collapse_document(hash["next"]) if hash["next"]
51
+ end
52
+ end
53
+ end
54
+
55
+ # Generate a Hash which breaks the recursive chain.
56
+ # Certain fields which are normally available are omitted.
57
+ #
58
+ # Returns a Hash with only non-recursive fields present.
59
+ def collapse_document(doc)
60
+ doc.keys.each_with_object({}) do |(key, _), result|
61
+ result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
62
+ end
63
+ end
64
+
30
65
  private
31
66
  def_delegator :@obj, :data, :fallback_data
32
67
  end
@@ -3,7 +3,9 @@
3
3
  module Jekyll
4
4
  module Drops
5
5
  class Drop < Liquid::Drop
6
- NON_CONTENT_METHODS = [:[], :[]=, :inspect, :to_h, :fallback_data].freeze
6
+ include Enumerable
7
+
8
+ NON_CONTENT_METHODS = [:fallback_data, :collapse_document].freeze
7
9
 
8
10
  # Get or set whether the drop class is mutable.
9
11
  # Mutability determines whether or not pre-defined fields may be
@@ -13,11 +15,11 @@ module Jekyll
13
15
  #
14
16
  # Returns the mutability of the class
15
17
  def self.mutable(is_mutable = nil)
16
- if is_mutable
17
- @is_mutable = is_mutable
18
- else
19
- @is_mutable = false
20
- end
18
+ @is_mutable = if is_mutable
19
+ is_mutable
20
+ else
21
+ false
22
+ end
21
23
  end
22
24
 
23
25
  def self.mutable?
@@ -86,7 +88,9 @@ module Jekyll
86
88
  # Returns an Array of strings which represent method-specific keys.
87
89
  def content_methods
88
90
  @content_methods ||= (
89
- self.class.instance_methods(false) - NON_CONTENT_METHODS
91
+ self.class.instance_methods \
92
+ - Jekyll::Drops::Drop.instance_methods \
93
+ - NON_CONTENT_METHODS
90
94
  ).map(&:to_s).reject do |method|
91
95
  method.end_with?("=")
92
96
  end
@@ -98,8 +102,8 @@ module Jekyll
98
102
  #
99
103
  # Returns true if the given key is present
100
104
  def key?(key)
101
- if self.class.mutable && @mutations.key?(key)
102
- true
105
+ if self.class.mutable
106
+ @mutations.key?(key)
103
107
  else
104
108
  respond_to?(key) || fallback_data.key?(key)
105
109
  end
@@ -134,10 +138,28 @@ module Jekyll
134
138
  #
135
139
  # Returns a pretty generation of the hash representation of the Drop.
136
140
  def inspect
137
- require 'json'
141
+ require "json"
138
142
  JSON.pretty_generate to_h
139
143
  end
140
144
 
145
+ # Generate a Hash for use in generating JSON.
146
+ # This is useful if fields need to be cleared before the JSON can generate.
147
+ #
148
+ # Returns a Hash ready for JSON generation.
149
+ def hash_for_json(*)
150
+ to_h
151
+ end
152
+
153
+ # Generate a JSON representation of the Drop.
154
+ #
155
+ # state - the JSON::State object which determines the state of current processing.
156
+ #
157
+ # Returns a JSON representation of the Drop in a String.
158
+ def to_json(state = nil)
159
+ require "json"
160
+ JSON.generate(hash_for_json(state), state)
161
+ end
162
+
141
163
  # Collects all the keys and passes each to the block in turn.
142
164
  #
143
165
  # block - a block which accepts one argument, the key
@@ -147,6 +169,12 @@ module Jekyll
147
169
  keys.each(&block)
148
170
  end
149
171
 
172
+ def each
173
+ each_key.each do |key|
174
+ yield key, self[key]
175
+ end
176
+ end
177
+
150
178
  def merge(other, &block)
151
179
  self.dup.tap do |me|
152
180
  if block.nil?
@@ -171,6 +199,17 @@ module Jekyll
171
199
  end
172
200
  end
173
201
  end
202
+
203
+ # Imitate Hash.fetch method in Drop
204
+ #
205
+ # Returns value if key is present in Drop, otherwise returns default value
206
+ # KeyError is raised if key is not present and no default value given
207
+ def fetch(key, default = nil, &block)
208
+ return self[key] if key?(key)
209
+ raise KeyError, %(key not found: "#{key}") if default.nil? && block.nil?
210
+ return yield(key) unless block.nil?
211
+ return default unless default.nil?
212
+ end
174
213
  end
175
214
  end
176
215
  end