Implement yaml data postscript
This commit is contained in:
parent
c0ddf15add
commit
7383f67874
|
@ -1,6 +1,9 @@
|
||||||
master
|
master
|
||||||
===
|
===
|
||||||
|
|
||||||
|
* Removed ability to use JSON as frontmatter. Still allowed in data/ folder.
|
||||||
|
* Added YAML data postscript. Like frontmatter, but reversed. Attach content after the key/value data as a `:postscript` key to the data structure (if Hash).
|
||||||
|
|
||||||
# 4.0.0.beta.2
|
# 4.0.0.beta.2
|
||||||
|
|
||||||
* Fixed regression causing exceptions to be silently thrown away outside of `--verbose` mode in the dev server.
|
* Fixed regression causing exceptions to be silently thrown away outside of `--verbose` mode in the dev server.
|
||||||
|
|
|
@ -51,3 +51,10 @@ Feature: Local Data API
|
||||||
Then I should see "title1:Hello"
|
Then I should see "title1:Hello"
|
||||||
Then I should see "title2:More"
|
Then I should see "title2:More"
|
||||||
Then I should see "title3:Stuff"
|
Then I should see "title3:Stuff"
|
||||||
|
|
||||||
|
Scenario: Using data postscript
|
||||||
|
Given the Server is running at "nested-data-app"
|
||||||
|
When I go to "/extracontent.html"
|
||||||
|
Then I should see "<h1>With Content</h1>"
|
||||||
|
Then I should see '<h2 id="header-2">Header 2</h2>'
|
||||||
|
Then I should see "<p>Paragraph 1</p>"
|
||||||
|
|
|
@ -31,17 +31,6 @@ Feature: Neighboring YAML Front Matter
|
||||||
When I go to "/front-matter-encoding.html.erb.frontmatter"
|
When I go to "/front-matter-encoding.html.erb.frontmatter"
|
||||||
Then I should see "File Not Found"
|
Then I should see "File Not Found"
|
||||||
|
|
||||||
Scenario: Rendering html (json)
|
|
||||||
Given the Server is running at "frontmatter-neighbor-app"
|
|
||||||
When I go to "/json-front-matter.html.erb.frontmatter"
|
|
||||||
Then I should see "File Not Found"
|
|
||||||
When I go to "/json-front-matter-2.php"
|
|
||||||
Then I should see "<h1>This is the title</h1>"
|
|
||||||
Then I should see "<?php"
|
|
||||||
Then I should not see ";;;"
|
|
||||||
When I go to "/json-front-matter-2.php.erb.frontmatter"
|
|
||||||
Then I should see "File Not Found"
|
|
||||||
|
|
||||||
Scenario: A template changes frontmatter during preview
|
Scenario: A template changes frontmatter during preview
|
||||||
Given the Server is running at "frontmatter-neighbor-app"
|
Given the Server is running at "frontmatter-neighbor-app"
|
||||||
And the file "source/front-matter-change.html.erb" has the contents
|
And the file "source/front-matter-change.html.erb" has the contents
|
||||||
|
|
|
@ -41,28 +41,6 @@ Feature: YAML Front Matter
|
||||||
Then I should see "<h1>This is the title</h1>"
|
Then I should see "<h1>This is the title</h1>"
|
||||||
Then I should not see "---"
|
Then I should not see "---"
|
||||||
|
|
||||||
Scenario: Rendering html (json)
|
|
||||||
Given the Server is running at "frontmatter-app"
|
|
||||||
When I go to "/json-front-matter.html"
|
|
||||||
Then I should see "<h1>This is the title</h1>"
|
|
||||||
Then I should not see ";;;"
|
|
||||||
When I go to "/json-front-matter-2.php"
|
|
||||||
Then I should see "<h1>This is the title</h1>"
|
|
||||||
Then I should see "<?php"
|
|
||||||
Then I should not see ";;;"
|
|
||||||
|
|
||||||
Scenario: JSON not on first line, no encoding
|
|
||||||
Given the Server is running at "frontmatter-app"
|
|
||||||
When I go to "/json-front-matter-line-2.html"
|
|
||||||
Then I should see "<h1></h1>"
|
|
||||||
Then I should see ";;;"
|
|
||||||
|
|
||||||
Scenario: JSON not on first line, with encoding
|
|
||||||
Given the Server is running at "frontmatter-app"
|
|
||||||
When I go to "/json-front-matter-encoding.html"
|
|
||||||
Then I should see "<h1>This is the title</h1>"
|
|
||||||
Then I should not see ";;;"
|
|
||||||
|
|
||||||
Scenario: A template changes frontmatter during preview
|
Scenario: A template changes frontmatter during preview
|
||||||
Given the Server is running at "frontmatter-app"
|
Given the Server is running at "frontmatter-app"
|
||||||
And the file "source/front-matter-change.html.erb" has the contents
|
And the file "source/front-matter-change.html.erb" has the contents
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<h2> Test</h2>
|
<h2> Test</h2>
|
||||||
|
|
||||||
|
<h1><%= current_page.data.title %></h1>
|
||||||
|
|
||||||
---
|
---
|
||||||
layout: false
|
layout: false
|
||||||
title: This is the title
|
title: This is the title
|
||||||
---
|
---
|
||||||
|
|
||||||
<h1><%= current_page.data.title %></h1>
|
<div>Stuff</div>
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
;;;
|
|
||||||
"layout": false,
|
|
||||||
"title": "This is the title"
|
|
||||||
;;;
|
|
||||||
|
|
||||||
<h1><%= current_page.data.title %></h1>
|
|
||||||
<?php echo "sup"; ?>
|
|
|
@ -1,7 +0,0 @@
|
||||||
# encoding: UTF-8
|
|
||||||
;;;
|
|
||||||
"layout": false,
|
|
||||||
"title": "This is the title"
|
|
||||||
;;;
|
|
||||||
|
|
||||||
<h1><%= current_page.data.title %></h1>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<h2> Test</h2>
|
|
||||||
;;;
|
|
||||||
layout: false,
|
|
||||||
title: "This is the title"
|
|
||||||
;;;
|
|
||||||
|
|
||||||
<h1><%= current_page.data.title %></h1>
|
|
|
@ -1,6 +0,0 @@
|
||||||
;;;
|
|
||||||
"layout": false,
|
|
||||||
"title": "This is the title"
|
|
||||||
;;;
|
|
||||||
|
|
||||||
<h1><%= current_page.data.title %></h1>
|
|
|
@ -15,7 +15,7 @@ class NeighborFrontmatter < ::Middleman::Extension
|
||||||
|
|
||||||
next unless file
|
next unless file
|
||||||
|
|
||||||
fmdata = app.extensions[:front_matter].frontmatter_and_content(file[:full_path]).first
|
fmdata = ::Middleman::Util::Data.parse(file[:full_path], :yaml).first
|
||||||
opts = fmdata.extract!(:layout, :layout_engine, :renderer_options, :directory_index, :content_type)
|
opts = fmdata.extract!(:layout, :layout_engine, :renderer_options, :directory_index, :content_type)
|
||||||
opts[:renderer_options].symbolize_keys! if opts.key?(:renderer_options)
|
opts[:renderer_options].symbolize_keys! if opts.key?(:renderer_options)
|
||||||
ignored = fmdata.delete(:ignored)
|
ignored = fmdata.delete(:ignored)
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
<h1><%= current_page.data.title %></h1>
|
|
||||||
<?php echo "sup"; ?>
|
|
|
@ -1,4 +0,0 @@
|
||||||
;;;
|
|
||||||
"layout": false,
|
|
||||||
"title": "This is the title"
|
|
||||||
;;;
|
|
|
@ -1 +0,0 @@
|
||||||
<h1><%= current_page.data.title %></h1>
|
|
|
@ -1,4 +0,0 @@
|
||||||
;;;
|
|
||||||
"layout": false,
|
|
||||||
"title": "This is the title"
|
|
||||||
;;;
|
|
|
@ -26,7 +26,7 @@ class NeighborFrontmatter < ::Middleman::Extension
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply_neighbor_data(resource, file)
|
def apply_neighbor_data(resource, file)
|
||||||
fmdata = app.extensions[:front_matter].frontmatter_and_content(file[:full_path]).first
|
fmdata = ::Middleman::Util::Data.parse(file[:full_path], :yaml).first
|
||||||
opts = fmdata.extract!(:layout, :layout_engine, :renderer_options, :directory_index, :content_type)
|
opts = fmdata.extract!(:layout, :layout_engine, :renderer_options, :directory_index, :content_type)
|
||||||
opts[:renderer_options].symbolize_keys! if opts.key?(:renderer_options)
|
opts[:renderer_options].symbolize_keys! if opts.key?(:renderer_options)
|
||||||
ignored = fmdata.delete(:ignored)
|
ignored = fmdata.delete(:ignored)
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
name: "With Content"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Header 2
|
||||||
|
|
||||||
|
Paragraph 1
|
||||||
|
|
||||||
|
Paragraph 2
|
||||||
|
|
||||||
|
### Header 3
|
|
@ -0,0 +1,4 @@
|
||||||
|
%h1= data.examples.withcontent.name
|
||||||
|
|
||||||
|
:markdown
|
||||||
|
<%= data.examples.withcontent.postscript.gsub("\n", "\n\s\s") %>
|
|
@ -33,7 +33,6 @@ module Middleman
|
||||||
# Set the value of a setting by key. Creates the setting if it doesn't exist.
|
# Set the value of a setting by key. Creates the setting if it doesn't exist.
|
||||||
# @param [Symbol] key
|
# @param [Symbol] key
|
||||||
# @param [Object] val
|
# @param [Object] val
|
||||||
# rubocop:disable UselessSetterCall
|
|
||||||
def []=(key, val)
|
def []=(key, val)
|
||||||
setting_obj = setting(key) || define_setting(key)
|
setting_obj = setting(key) || define_setting(key)
|
||||||
setting_obj.value = val
|
setting_obj.value = val
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
require 'yaml'
|
|
||||||
require 'active_support/json'
|
|
||||||
require 'middleman-core/contracts'
|
require 'middleman-core/contracts'
|
||||||
|
require 'middleman-core/util/data'
|
||||||
|
|
||||||
module Middleman
|
module Middleman
|
||||||
module CoreExtensions
|
module CoreExtensions
|
||||||
|
@ -100,9 +99,10 @@ module Middleman
|
||||||
basename = File.basename(data_path, extension)
|
basename = File.basename(data_path, extension)
|
||||||
|
|
||||||
if %w(.yaml .yml).include?(extension)
|
if %w(.yaml .yml).include?(extension)
|
||||||
data = ::YAML.load_file(file[:full_path])
|
data, postscript = ::Middleman::Util::Data.parse(file[:full_path], :yaml)
|
||||||
|
data[:postscript] = postscript if !postscript.nil? && data.is_a?(Hash)
|
||||||
elsif extension == '.json'
|
elsif extension == '.json'
|
||||||
data = ::ActiveSupport::JSON.decode(file[:full_path].read)
|
data, _postscript = ::Middleman::Util::Data.parse(file[:full_path], :json)
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
require 'active_support/core_ext/hash/keys'
|
# Core Pathname library used for traversal
|
||||||
require 'pathname'
|
require 'pathname'
|
||||||
|
|
||||||
# Parsing YAML frontmatter
|
# DbC
|
||||||
require 'yaml'
|
require 'middleman-core/contracts'
|
||||||
|
|
||||||
# Parsing JSON frontmatter
|
require 'active_support/core_ext/hash/keys'
|
||||||
require 'active_support/json'
|
|
||||||
|
require 'middleman-core/util/data'
|
||||||
|
|
||||||
# Extensions namespace
|
# Extensions namespace
|
||||||
module Middleman::CoreExtensions
|
module Middleman::CoreExtensions
|
||||||
|
@ -13,13 +14,6 @@ module Middleman::CoreExtensions
|
||||||
# Try to run after routing but before directory_indexes
|
# Try to run after routing but before directory_indexes
|
||||||
self.resource_list_manipulator_priority = 90
|
self.resource_list_manipulator_priority = 90
|
||||||
|
|
||||||
YAML_ERRORS = [StandardError]
|
|
||||||
|
|
||||||
# https://github.com/tenderlove/psych/issues/23
|
|
||||||
if defined?(Psych) && defined?(Psych::SyntaxError)
|
|
||||||
YAML_ERRORS << Psych::SyntaxError
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(app, options_hash={}, &block)
|
def initialize(app, options_hash={}, &block)
|
||||||
super
|
super
|
||||||
|
|
||||||
|
@ -71,7 +65,7 @@ module Middleman::CoreExtensions
|
||||||
|
|
||||||
return [{}, nil] unless file
|
return [{}, nil] unless file
|
||||||
|
|
||||||
@cache[file[:full_path]] ||= frontmatter_and_content(file[:full_path])
|
@cache[file[:full_path]] ||= ::Middleman::Util::Data.parse(file[:full_path])
|
||||||
end
|
end
|
||||||
|
|
||||||
Contract ArrayOf[IsA['Middleman::SourceFile']], ArrayOf[IsA['Middleman::SourceFile']] => Any
|
Contract ArrayOf[IsA['Middleman::SourceFile']], ArrayOf[IsA['Middleman::SourceFile']] => Any
|
||||||
|
@ -80,93 +74,5 @@ module Middleman::CoreExtensions
|
||||||
@cache.delete(file[:full_path])
|
@cache.delete(file[:full_path])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the frontmatter and plain content from a file
|
|
||||||
# @param [String] path
|
|
||||||
# @return [Array<Middleman::Util::IndifferentHash, String>]
|
|
||||||
Contract Pathname => [Hash, Maybe[String]]
|
|
||||||
def frontmatter_and_content(full_path)
|
|
||||||
data = {}
|
|
||||||
|
|
||||||
return [data, nil] if ::Middleman::Util.binary?(full_path)
|
|
||||||
|
|
||||||
# Avoid weird race condition when a file is renamed.
|
|
||||||
content = begin
|
|
||||||
File.read(full_path)
|
|
||||||
rescue ::EOFError
|
|
||||||
rescue ::IOError
|
|
||||||
rescue ::Errno::ENOENT
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
if content =~ /\A.*coding:/
|
|
||||||
lines = content.split(/\n/)
|
|
||||||
lines.shift
|
|
||||||
content = lines.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
result = parse_yaml_front_matter(content, full_path) || parse_json_front_matter(content, full_path)
|
|
||||||
return result if result
|
|
||||||
rescue
|
|
||||||
# Probably a binary file, move on
|
|
||||||
end
|
|
||||||
|
|
||||||
[data, content]
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Parse YAML frontmatter out of a string
|
|
||||||
# @param [String] content
|
|
||||||
# @return [Array<Hash, String>]
|
|
||||||
Contract String, Pathname => Maybe[[Hash, String]]
|
|
||||||
def parse_yaml_front_matter(content, full_path)
|
|
||||||
yaml_regex = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
|
|
||||||
if content =~ yaml_regex
|
|
||||||
content = content.sub(yaml_regex, '')
|
|
||||||
|
|
||||||
begin
|
|
||||||
data = YAML.load($1) || {}
|
|
||||||
data = data.symbolize_keys
|
|
||||||
rescue *YAML_ERRORS => e
|
|
||||||
app.logger.error "YAML Exception parsing #{full_path}: #{e.message}"
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
[data, content]
|
|
||||||
rescue
|
|
||||||
[{}, content]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse JSON frontmatter out of a string
|
|
||||||
# @param [String] content
|
|
||||||
# @return [Array<Hash, String>]
|
|
||||||
Contract String, Pathname => Maybe[[Hash, String]]
|
|
||||||
def parse_json_front_matter(content, full_path)
|
|
||||||
json_regex = /\A(;;;\s*\n.*?\n?)^(;;;\s*$\n?)/m
|
|
||||||
|
|
||||||
if content =~ json_regex
|
|
||||||
content = content.sub(json_regex, '')
|
|
||||||
|
|
||||||
begin
|
|
||||||
json = ($1 + $2).sub(';;;', '{').sub(';;;', '}')
|
|
||||||
data = ::ActiveSupport::JSON.decode(json).symbolize_keys
|
|
||||||
rescue => e
|
|
||||||
app.logger.error "JSON Exception parsing #{full_path}: #{e.message}"
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
[data, content]
|
|
||||||
rescue
|
|
||||||
[{}, content]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,7 +27,26 @@ module Middleman
|
||||||
next unless resource.source_file[:relative_path].to_s =~ %r{\.liquid$}
|
next unless resource.source_file[:relative_path].to_s =~ %r{\.liquid$}
|
||||||
|
|
||||||
# Convert data object into a hash for liquid
|
# Convert data object into a hash for liquid
|
||||||
resource.add_metadata locals: { data: app.extensions[:data].data_store.to_h }
|
resource.add_metadata locals: {
|
||||||
|
data: stringify_recursive(app.extensions[:data].data_store.to_h)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def stringify_recursive(hash)
|
||||||
|
{}.tap do |h|
|
||||||
|
hash.each { |key, value| h[key.to_s] = map_value(value) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def map_value(thing)
|
||||||
|
case thing
|
||||||
|
when Hash
|
||||||
|
stringify_recursive(thing)
|
||||||
|
when Array
|
||||||
|
thing.map { |v| map_value(v) }
|
||||||
|
else
|
||||||
|
thing
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
153
middleman-core/lib/middleman-core/util/data.rb
Normal file
153
middleman-core/lib/middleman-core/util/data.rb
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
# Core Pathname library used for traversal
|
||||||
|
require 'pathname'
|
||||||
|
|
||||||
|
# DbC
|
||||||
|
require 'middleman-core/contracts'
|
||||||
|
|
||||||
|
# Shared util methods
|
||||||
|
require 'middleman-core/util'
|
||||||
|
|
||||||
|
# Parsing YAML data
|
||||||
|
require 'yaml'
|
||||||
|
|
||||||
|
# Parsing JSON data
|
||||||
|
require 'active_support/json'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
module Util
|
||||||
|
module Data
|
||||||
|
include Contracts
|
||||||
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
YAML_ERRORS = [StandardError]
|
||||||
|
|
||||||
|
# https://github.com/tenderlove/psych/issues/23
|
||||||
|
if defined?(Psych) && defined?(Psych::SyntaxError)
|
||||||
|
YAML_ERRORS << Psych::SyntaxError
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the frontmatter and plain content from a file
|
||||||
|
# @param [String] path
|
||||||
|
# @return [Array<Middleman::Util::IndifferentHash, String>]
|
||||||
|
Contract Pathname, Maybe[Symbol] => [Hash, Maybe[String]]
|
||||||
|
def parse(full_path, known_type=nil)
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
return [data, nil] if ::Middleman::Util.binary?(full_path)
|
||||||
|
|
||||||
|
# Avoid weird race condition when a file is renamed.
|
||||||
|
content = begin
|
||||||
|
File.read(full_path)
|
||||||
|
rescue ::EOFError
|
||||||
|
rescue ::IOError
|
||||||
|
rescue ::Errno::ENOENT
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
if content =~ /\A.*coding:/
|
||||||
|
lines = content.split(/\n/)
|
||||||
|
lines.shift
|
||||||
|
content = lines.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
if known_type
|
||||||
|
if known_type == :yaml
|
||||||
|
result = parse_yaml(content, full_path, true)
|
||||||
|
elsif known_type == :json
|
||||||
|
result = parse_json(content, full_path)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result = parse_yaml(content, full_path, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
return result if result
|
||||||
|
rescue
|
||||||
|
# Probably a binary file, move on
|
||||||
|
end
|
||||||
|
|
||||||
|
[data, content]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse YAML frontmatter out of a string
|
||||||
|
# @param [String] content
|
||||||
|
# @return [Array<Hash, String>]
|
||||||
|
Contract String, Pathname, Bool => Maybe[[Hash, String]]
|
||||||
|
def parse_yaml(content, full_path, require_yaml=false)
|
||||||
|
total_delims = content.scan(/^(?:---|\.\.\.)\s*(?:\n|$)/).length
|
||||||
|
has_first_line_delim = !content.match(/\A(---\s*(?:\n|$))/).nil?
|
||||||
|
# has_closing_delim = (total_delims > 1 && has_first_line_delim) || (!has_first_line_delim && total_delims == 1)
|
||||||
|
|
||||||
|
parts = content.split(/^(?:---|\.\.\.)\s*(?:\n|$)/)
|
||||||
|
parts.shift if parts[0].empty?
|
||||||
|
|
||||||
|
yaml_string = nil
|
||||||
|
additional_content = nil
|
||||||
|
|
||||||
|
if require_yaml
|
||||||
|
yaml_string = parts[0]
|
||||||
|
additional_content = parts[1]
|
||||||
|
else
|
||||||
|
if total_delims > 1
|
||||||
|
if has_first_line_delim
|
||||||
|
yaml_string = parts[0]
|
||||||
|
additional_content = parts[1]
|
||||||
|
else
|
||||||
|
additional_content = content
|
||||||
|
end
|
||||||
|
else
|
||||||
|
additional_content = parts[0]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return [{}, additional_content] if yaml_string.nil?
|
||||||
|
|
||||||
|
begin
|
||||||
|
data = map_value(::YAML.load(yaml_string) || {})
|
||||||
|
rescue *YAML_ERRORS => e
|
||||||
|
$stderr.puts "YAML Exception parsing #{full_path}: #{e.message}"
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
[data, additional_content]
|
||||||
|
rescue
|
||||||
|
[{}, additional_content]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse JSON frontmatter out of a string
|
||||||
|
# @param [String] content
|
||||||
|
# @return [Array<Hash, String>]
|
||||||
|
Contract String, Pathname => Maybe[[Hash, String]]
|
||||||
|
def parse_json(content, full_path)
|
||||||
|
begin
|
||||||
|
data = map_value(::ActiveSupport::JSON.decode(content))
|
||||||
|
rescue => e
|
||||||
|
$stderr.puts "JSON Exception parsing #{full_path}: #{e.message}"
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
[data, nil]
|
||||||
|
rescue
|
||||||
|
[{}, nil]
|
||||||
|
end
|
||||||
|
|
||||||
|
def symbolize_recursive(hash)
|
||||||
|
{}.tap do |h|
|
||||||
|
hash.each { |key, value| h[key.to_sym] = map_value(value) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def map_value(thing)
|
||||||
|
case thing
|
||||||
|
when Hash
|
||||||
|
symbolize_recursive(thing)
|
||||||
|
when Array
|
||||||
|
thing.map { |v| map_value(v) }
|
||||||
|
else
|
||||||
|
thing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue