Add ability to render drops as JSON
Previously you could do, e.g. {{ site | jsonify }}, but with the introduction of Liquid Drops, this didn't work anymore. This PR adds the ability to render drops as JSON. You can safely run drop.to_json and it should Do the Right Thing.
This commit is contained in:
parent
e02049727b
commit
17d8c96a63
|
@ -5,10 +5,12 @@ module Jekyll
|
||||||
class DocumentDrop < Drop
|
class DocumentDrop < Drop
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
|
|
||||||
|
NESTED_OBJECT_FIELD_BLACKLIST = %w{
|
||||||
|
content output excerpt next previous
|
||||||
|
}.freeze
|
||||||
|
|
||||||
mutable false
|
mutable false
|
||||||
|
|
||||||
def_delegator :@obj, :next_doc, :next
|
|
||||||
def_delegator :@obj, :previous_doc, :previous
|
|
||||||
def_delegator :@obj, :relative_path, :path
|
def_delegator :@obj, :relative_path, :path
|
||||||
def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url
|
def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url
|
||||||
|
|
||||||
|
@ -27,6 +29,37 @@ module Jekyll
|
||||||
cmp
|
cmp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def previous
|
||||||
|
@obj.previous_doc.to_liquid
|
||||||
|
end
|
||||||
|
|
||||||
|
def next
|
||||||
|
@obj.next_doc.to_liquid
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a Hash for use in generating JSON.
|
||||||
|
# This is useful if fields need to be cleared before the JSON can generate.
|
||||||
|
#
|
||||||
|
# Returns a Hash ready for JSON generation.
|
||||||
|
def hash_for_json(state = nil)
|
||||||
|
to_h.tap do |hash|
|
||||||
|
if state && state.depth >= 2
|
||||||
|
hash["previous"] = collapse_document(hash["previous"]) if hash["previous"]
|
||||||
|
hash["next"] = collapse_document(hash["next"]) if hash["next"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a Hash which breaks the recursive chain.
|
||||||
|
# Certain fields which are normally available are omitted.
|
||||||
|
#
|
||||||
|
# Returns a Hash with only non-recursive fields present.
|
||||||
|
def collapse_document(doc)
|
||||||
|
doc.keys.each_with_object({}) do |(key, _), result|
|
||||||
|
result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def_delegator :@obj, :data, :fallback_data
|
def_delegator :@obj, :data, :fallback_data
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
module Jekyll
|
module Jekyll
|
||||||
module Drops
|
module Drops
|
||||||
class Drop < Liquid::Drop
|
class Drop < Liquid::Drop
|
||||||
NON_CONTENT_METHODS = [:[], :[]=, :inspect, :to_h, :fallback_data].freeze
|
include Enumerable
|
||||||
|
|
||||||
|
NON_CONTENT_METHODS = [:[], :[]=, :inspect, :to_h, :fallback_data, :collapse_document].freeze
|
||||||
|
|
||||||
# Get or set whether the drop class is mutable.
|
# Get or set whether the drop class is mutable.
|
||||||
# Mutability determines whether or not pre-defined fields may be
|
# Mutability determines whether or not pre-defined fields may be
|
||||||
|
@ -138,6 +140,22 @@ module Jekyll
|
||||||
JSON.pretty_generate to_h
|
JSON.pretty_generate to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generate a Hash for use in generating JSON.
|
||||||
|
# This is useful if fields need to be cleared before the JSON can generate.
|
||||||
|
#
|
||||||
|
# Returns a Hash ready for JSON generation.
|
||||||
|
def hash_for_json(state = nil)
|
||||||
|
to_h
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a JSON representation of the Drop.
|
||||||
|
#
|
||||||
|
# Returns a JSON representation of the Drop in a String.
|
||||||
|
def to_json(state = nil)
|
||||||
|
require 'json'
|
||||||
|
JSON.generate(hash_for_json(state), state)
|
||||||
|
end
|
||||||
|
|
||||||
# Collects all the keys and passes each to the block in turn.
|
# Collects all the keys and passes each to the block in turn.
|
||||||
#
|
#
|
||||||
# block - a block which accepts one argument, the key
|
# block - a block which accepts one argument, the key
|
||||||
|
|
|
@ -16,6 +16,18 @@ module Jekyll
|
||||||
def environment
|
def environment
|
||||||
Jekyll.env
|
Jekyll.env
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
@to_h ||= {
|
||||||
|
"version" => version,
|
||||||
|
"environment" => environment
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_json(state = nil)
|
||||||
|
require 'json'
|
||||||
|
JSON.generate(to_h, state)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -279,6 +279,7 @@ module Jekyll
|
||||||
def site_payload
|
def site_payload
|
||||||
Drops::UnifiedPayloadDrop.new self
|
Drops::UnifiedPayloadDrop.new self
|
||||||
end
|
end
|
||||||
|
alias_method :to_liquid, :site_payload
|
||||||
|
|
||||||
# Get the implementation class for the given Converter.
|
# Get the implementation class for the given Converter.
|
||||||
# Returns the Converter instance implementing the given Converter.
|
# Returns the Converter instance implementing the given Converter.
|
||||||
|
|
|
@ -44,6 +44,46 @@ class TestDocument < JekyllUnitTest
|
||||||
assert_equal "foo.bar", @document.data["whatever"]
|
assert_equal "foo.bar", @document.data["whatever"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "be jsonify-able" do
|
||||||
|
page_json = @document.to_liquid.to_json
|
||||||
|
parsed = JSON.parse(page_json)
|
||||||
|
|
||||||
|
assert_equal "Jekyll.configuration", parsed["title"]
|
||||||
|
assert_equal "foo.bar", parsed["whatever"]
|
||||||
|
assert_equal nil, parsed["previous"]
|
||||||
|
|
||||||
|
next_doc = parsed["next"]
|
||||||
|
assert_equal "_methods/escape-+ #%20[].md", next_doc["path"]
|
||||||
|
assert_equal "Jekyll.escape", next_doc["title"]
|
||||||
|
|
||||||
|
next_prev_doc = next_doc["previous"]
|
||||||
|
assert_equal "Jekyll.configuration", next_prev_doc["title"]
|
||||||
|
assert_equal "_methods/configuration.md", next_prev_doc["path"]
|
||||||
|
assert_equal "_methods/escape-+ #%20[].md", next_prev_doc["next"]["path"]
|
||||||
|
assert_nil next_prev_doc["previous"] # nothing before Jekyll.configuration
|
||||||
|
assert_nil next_prev_doc["next"]["next"]
|
||||||
|
assert_nil next_prev_doc["next"]["previous"]
|
||||||
|
assert_nil next_prev_doc["next"]["content"]
|
||||||
|
assert_nil next_prev_doc["next"]["excerpt"]
|
||||||
|
assert_nil next_prev_doc["next"]["output"]
|
||||||
|
|
||||||
|
next_next_doc = next_doc["next"]
|
||||||
|
assert_equal "Jekyll.sanitized_path", next_next_doc["title"]
|
||||||
|
assert_equal "_methods/sanitized_path.md", next_next_doc["path"]
|
||||||
|
assert_equal "_methods/escape-+ #%20[].md", next_next_doc["previous"]["path"]
|
||||||
|
assert_equal "_methods/site/generate.md", next_next_doc["next"]["path"]
|
||||||
|
assert_nil next_next_doc["next"]["next"]
|
||||||
|
assert_nil next_next_doc["next"]["previous"]
|
||||||
|
assert_nil next_next_doc["next"]["content"]
|
||||||
|
assert_nil next_next_doc["next"]["excerpt"]
|
||||||
|
assert_nil next_next_doc["next"]["output"]
|
||||||
|
assert_nil next_next_doc["previous"]["next"]
|
||||||
|
assert_nil next_next_doc["previous"]["previous"]
|
||||||
|
assert_nil next_next_doc["previous"]["content"]
|
||||||
|
assert_nil next_next_doc["previous"]["excerpt"]
|
||||||
|
assert_nil next_next_doc["previous"]["output"]
|
||||||
|
end
|
||||||
|
|
||||||
context "with YAML ending in three dots" do
|
context "with YAML ending in three dots" do
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
|
|
|
@ -198,6 +198,44 @@ class TestFilters < JekyllUnitTest
|
||||||
assert_equal "[{\"name\":\"Jack\"},{\"name\":\"Smith\"}]", @filter.jsonify([{:name => 'Jack'}, {:name => 'Smith'}])
|
assert_equal "[{\"name\":\"Jack\"},{\"name\":\"Smith\"}]", @filter.jsonify([{:name => 'Jack'}, {:name => 'Smith'}])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "convert drop to json" do
|
||||||
|
@filter.site.read
|
||||||
|
expected = {
|
||||||
|
"next" => "Categories _should_ work",
|
||||||
|
"path" => "_posts/2008-02-02-published.markdown",
|
||||||
|
"output" => nil,
|
||||||
|
"previous" => nil,
|
||||||
|
"content" => "This should be published.\n",
|
||||||
|
"id" => "/publish_test/2008/02/02/published",
|
||||||
|
"url" => "/publish_test/2008/02/02/published.html",
|
||||||
|
"relative_path" => "_posts/2008-02-02-published.markdown",
|
||||||
|
"collection" => "posts",
|
||||||
|
"excerpt" => "<p>This should be published.</p>\n",
|
||||||
|
"draft" => false,
|
||||||
|
"categories" => [
|
||||||
|
"publish_test"
|
||||||
|
],
|
||||||
|
"layout" => "default",
|
||||||
|
"title" => "Publish",
|
||||||
|
"category" => "publish_test",
|
||||||
|
"date" => "2008-02-02 00:00:00 +0000",
|
||||||
|
"slug" => "published",
|
||||||
|
"ext" => ".markdown",
|
||||||
|
"tags" => []
|
||||||
|
}
|
||||||
|
actual = @filter.jsonify(@filter.site.docs_to_write.first.to_liquid)
|
||||||
|
assert_equal expected, JSON.parse(actual)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "convert drop with drops to json" do
|
||||||
|
@filter.site.read
|
||||||
|
actual = @filter.jsonify(@filter.site.to_liquid)
|
||||||
|
assert_equal JSON.parse(actual)["jekyll"], {
|
||||||
|
"environment" => "development",
|
||||||
|
"version" => Jekyll::VERSION
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
class M < Struct.new(:message)
|
class M < Struct.new(:message)
|
||||||
def to_liquid
|
def to_liquid
|
||||||
[message]
|
[message]
|
||||||
|
|
Loading…
Reference in New Issue