/
data.rb
135 lines (119 loc) · 3.73 KB
/
data.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require 'yaml'
require 'json'
require 'pathname'
require 'backports/2.1.0/array/to_h'
require 'hashie'
require 'middleman-core/util/binary'
require 'middleman-core/contracts'
module Middleman
module Util
include Contracts
module_function
class EnhancedHash < ::Hashie::Mash
# include ::Hashie::Extensions::MergeInitializer
# include ::Hashie::Extensions::MethodReader
# include ::Hashie::Extensions::IndifferentAccess
end
# Recursively convert a normal Hash into a EnhancedHash
#
# @private
# @param [Hash] data Normal hash
# @return [Hash]
Contract Any => Maybe[Or[Array, EnhancedHash]]
def recursively_enhance(obj)
if obj.is_a? ::Array
obj.map { |e| recursively_enhance(e) }
elsif obj.is_a? ::Hash
::Hashie::Mash.new(obj)
else
obj
end
end
module Data
include Contracts
module_function
# Get the frontmatter and plain content from a file
# @param [String] path
# @return [Array<Hash, String>]
Contract IsA['Middleman::SourceFile'], Maybe[Symbol] => [Hash, Maybe[String]]
def parse(file, frontmatter_delims, known_type=nil)
full_path = file[:full_path]
return [{}, nil] if ::Middleman::Util.binary?(full_path) || file[:types].include?(:binary)
# Avoid weird race condition when a file is renamed
begin
content = file.read
rescue EOFError, IOError, ::Errno::ENOENT
return [{}, nil]
end
start_delims, stop_delims = frontmatter_delims
.values
.flatten(1)
.transpose
.map(&::Regexp.method(:union))
match = /
\A(?:[^\r\n]*coding:[^\r\n]*\r?\n)?
(?<start>#{start_delims})[ ]*\r?\n
(?<frontmatter>.*?)[ ]*\r?\n?
^(?<stop>#{stop_delims})[ ]*\r?\n?
\r?\n?
(?<additional_content>.*)
/mx.match(content) || {}
unless match[:frontmatter]
case known_type
when :yaml
return [parse_yaml(content, full_path), nil]
when :json
return [parse_json(content, full_path), nil]
end
end
case [match[:start], match[:stop]]
when *frontmatter_delims[:yaml]
[
parse_yaml(match[:frontmatter], full_path),
match[:additional_content]
]
when *frontmatter_delims[:json]
[
parse_json("{#{match[:frontmatter]}}", full_path),
match[:additional_content]
]
else
[
{},
content
]
end
end
# Parse YAML frontmatter out of a string
# @param [String] content
# @return [Hash]
Contract String, Pathname, Bool => Hash
def parse_yaml(content, full_path)
symbolize_recursive(::YAML.load(content) || {})
rescue StandardError, ::Psych::SyntaxError => error
warn "YAML Exception parsing #{full_path}: #{error.message}"
{}
end
# Parse JSON frontmatter out of a string
# @param [String] content
# @return [Hash]
Contract String, Pathname => Hash
def parse_json(content, full_path)
symbolize_recursive(::JSON.parse(content) || {})
rescue StandardError => error
warn "JSON Exception parsing #{full_path}: #{error.message}"
{}
end
def symbolize_recursive(value)
case value
when Hash
value.map { |k, v| [k.to_sym, symbolize_recursive(v)] }.to_h
when Array
value.map { |v| symbolize_recursive(v) }
else
value
end
end
end
end
end