jekyll-liquid-plus 1.0.0
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.
- checksums.yaml +15 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +345 -0
- data/Rakefile +1 -0
- data/jekyll-liquid-plus.gemspec +24 -0
- data/lib/jekyll-liquid-plus.rb +20 -0
- data/lib/jekyll-liquid-plus/helpers/conditional.rb +43 -0
- data/lib/jekyll-liquid-plus/helpers/include.rb +46 -0
- data/lib/jekyll-liquid-plus/helpers/path.rb +73 -0
- data/lib/jekyll-liquid-plus/helpers/var.rb +47 -0
- data/lib/jekyll-liquid-plus/tags/assign.rb +19 -0
- data/lib/jekyll-liquid-plus/tags/capture.rb +34 -0
- data/lib/jekyll-liquid-plus/tags/include.rb +16 -0
- data/lib/jekyll-liquid-plus/tags/render.rb +133 -0
- data/lib/jekyll-liquid-plus/tags/return.rb +20 -0
- data/lib/jekyll-liquid-plus/tags/wrap.rb +36 -0
- data/lib/jekyll-liquid-plus/version.rb +3 -0
- data/test/_config.yml +4 -0
- data/test/source/.gitignore +1 -0
- data/test/source/_includes/file.html +1 -0
- data/test/source/_includes/file2.html +1 -0
- data/test/source/_layouts/default.html +11 -0
- data/test/source/_plugins/liquid-plus.rb +2 -0
- data/test/source/assign.html +30 -0
- data/test/source/capture.html +29 -0
- data/test/source/f/file.html +1 -0
- data/test/source/f/file2.html +1 -0
- data/test/source/f/file3.md +6 -0
- data/test/source/include.html +31 -0
- data/test/source/render.html +36 -0
- data/test/source/return.html +24 -0
- data/test/source/wrap.html +29 -0
- data/test/test.rb +44 -0
- metadata +135 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module LiquidPlus
|
2
|
+
class Conditional
|
3
|
+
EXPRESSION = /(.+?)\s+(unless|if)\s+(.+)/i
|
4
|
+
TERNARY = /(.*?)\(\s*(.+?)\s+\?\s+(.+?)\s+:\s+(.+?)\s*\)(.+)?/
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def parse(markup, context)
|
8
|
+
strip_expression(markup, context) if evaluate_expression markup, context
|
9
|
+
end
|
10
|
+
|
11
|
+
def strip_expression(markup, context = false)
|
12
|
+
if markup =~ TERNARY
|
13
|
+
# Pick winner from ternary statement
|
14
|
+
result = evaluate_ternary($2, $3, $4, context)
|
15
|
+
markup = "#{$1} #{result} #{$5}"
|
16
|
+
end
|
17
|
+
markup =~ EXPRESSION ? $1 : markup
|
18
|
+
end
|
19
|
+
|
20
|
+
def evaluate_ternary(expression, if_true, if_false, context)
|
21
|
+
evaluate('if', expression, context) ? if_true : if_false
|
22
|
+
end
|
23
|
+
|
24
|
+
def evaluate_expression(markup, context)
|
25
|
+
if markup =~ EXPRESSION
|
26
|
+
evaluate($2, $3.gsub(/ \|\| /, ' or '), context)
|
27
|
+
else
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def evaluate(type, expression, context)
|
33
|
+
tag = if type == 'if'
|
34
|
+
Liquid::If.new('if', expression, ["true","{% endif %}"])
|
35
|
+
elsif type == 'unless'
|
36
|
+
Liquid::Unless.new('unless', expression, ["true","{% endunless %}"])
|
37
|
+
end
|
38
|
+
tag.render(context) != ''
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/path'
|
2
|
+
|
3
|
+
module LiquidPlus
|
4
|
+
class Include
|
5
|
+
LOCALS = /(.*?)(([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+)))(.*)?/
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def render(markup, context)
|
10
|
+
|
11
|
+
markup, params = split_params markup
|
12
|
+
file = Path.parse(markup, context, '_includes')
|
13
|
+
|
14
|
+
if file
|
15
|
+
if File.exist? Path.expand(File.join('_includes', file), context)
|
16
|
+
markup = file
|
17
|
+
markup += params if params
|
18
|
+
tag = Jekyll::Tags::IncludeTag.new('', markup, [])
|
19
|
+
tag.render(context)
|
20
|
+
else
|
21
|
+
dir = '_includes'
|
22
|
+
dir = File.join dir, File.dirname(file) unless File.dirname(file) == '.'
|
23
|
+
|
24
|
+
msg = "From #{context.registers[:page]['path']}: "
|
25
|
+
msg += "File '#{file}' not found"
|
26
|
+
msg += " in '#{dir}/' directory"
|
27
|
+
|
28
|
+
puts msg.red
|
29
|
+
return msg
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def split_params(markup)
|
35
|
+
params = nil
|
36
|
+
if markup =~ LOCALS
|
37
|
+
params = ' ' + $2
|
38
|
+
markup = $1 + $7
|
39
|
+
end
|
40
|
+
[markup, params]
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/conditional'
|
2
|
+
|
3
|
+
module LiquidPlus
|
4
|
+
class Path
|
5
|
+
class << self
|
6
|
+
|
7
|
+
#
|
8
|
+
def parse(markup, context, path=nil)
|
9
|
+
files = Conditional.parse(markup, context)
|
10
|
+
if files
|
11
|
+
select(files, context, path)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Allow paths to begin from the directory of the context page
|
16
|
+
#
|
17
|
+
# Input:
|
18
|
+
# - file: "file.html"
|
19
|
+
# - context: A Jekyll context object
|
20
|
+
#
|
21
|
+
# Returns the full path to a file
|
22
|
+
#
|
23
|
+
def expand(file, context)
|
24
|
+
root = context.registers[:site].source
|
25
|
+
if file =~ /^\.\/(.+)/
|
26
|
+
local_dir = File.dirname context.registers[:page]['path']
|
27
|
+
File.join root, local_dir, $1
|
28
|
+
else
|
29
|
+
File.join root, file
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Find the first file which exists
|
34
|
+
#
|
35
|
+
# Input:
|
36
|
+
# - file: "file.html || some_var || path/to/file.md" - a list of files
|
37
|
+
# - context: A Jekyll context object
|
38
|
+
# - path: e.g. "_includes" - a directory to prepend to the path
|
39
|
+
#
|
40
|
+
# Return the first file path which exists, or the last if none exist
|
41
|
+
# If the last path is 'none' this returns false
|
42
|
+
#
|
43
|
+
def select(files, context, path)
|
44
|
+
files = get_paths(files, context)
|
45
|
+
files.each_with_index do |f, i|
|
46
|
+
file = path ? File.join(path, f) : f
|
47
|
+
if File.exist? expand(file, context)
|
48
|
+
return f
|
49
|
+
# If "file.html || none" is passed, fail gracefully
|
50
|
+
elsif i == files.size - 1
|
51
|
+
return f.downcase == 'none' ? false : f
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Read file paths from context variables if necessary
|
57
|
+
#
|
58
|
+
# Input:
|
59
|
+
# - file: file.html || some_var || path/to/file.md
|
60
|
+
# - context: A Jekyll context object
|
61
|
+
#
|
62
|
+
# Returns a list of file paths
|
63
|
+
#
|
64
|
+
def get_paths(files, context)
|
65
|
+
files = files.split(/ (\|\||or) /).map do |file|
|
66
|
+
file = file.strip
|
67
|
+
context[file].nil? ? file : context[file]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/conditional'
|
2
|
+
|
3
|
+
module LiquidPlus
|
4
|
+
class Var
|
5
|
+
SYNTAX = /(#{Liquid::VariableSignature}+)\s*(=|\+=|\|\|=)\s*(.*)\s*/o
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def parse(markup, context)
|
9
|
+
if markup = Conditional.parse(markup, context)
|
10
|
+
if markup =~ SYNTAX
|
11
|
+
@to = $1
|
12
|
+
@operator = $2
|
13
|
+
@from = $3
|
14
|
+
|
15
|
+
value = get_value(@from, context)
|
16
|
+
|
17
|
+
if @operator == '+=' and !context.scopes.last[@to].nil?
|
18
|
+
context.scopes.last[@to] += value
|
19
|
+
elsif @operator == '=' or (@operator == '||=' and context.scopes.last[@to].nil?)
|
20
|
+
context.scopes.last[@to] = value
|
21
|
+
end
|
22
|
+
context
|
23
|
+
else
|
24
|
+
raise SyntaxError.new("Syntax Error in 'assign' - Valid syntax: assign [var] = [source]")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def determine_value(vars, context)
|
30
|
+
vars.each do |var|
|
31
|
+
rendered = var.render(context)
|
32
|
+
return rendered unless rendered.nil?
|
33
|
+
end
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_value(vars, context)
|
38
|
+
vars = vars.gsub(/ or /, ' || ')
|
39
|
+
vars = vars.strip.split(/ \|\| /).map do |v|
|
40
|
+
Liquid::Variable.new(v.strip)
|
41
|
+
end
|
42
|
+
determine_value(vars, context)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/var'
|
2
|
+
|
3
|
+
module LiquidPlus
|
4
|
+
class AssignTag < Liquid::Tag
|
5
|
+
|
6
|
+
def initialize(tag_name, markup, tokens)
|
7
|
+
@markup = markup
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def render(context)
|
12
|
+
if c = Var.parse(@markup, context)
|
13
|
+
context = c
|
14
|
+
end
|
15
|
+
''
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/conditional'
|
2
|
+
|
3
|
+
module LiquidPlus
|
4
|
+
class CaptureTag < Liquid::Block
|
5
|
+
WORD_REGEX = '[[:word:]]'
|
6
|
+
SYNTAX = /(#{WORD_REGEX}+)\s*(\+=|\|\|=)?/o
|
7
|
+
|
8
|
+
def initialize(tag_name, markup, tokens)
|
9
|
+
@markup = markup
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def render(context)
|
14
|
+
if @markup = Conditional.parse(@markup, context)
|
15
|
+
if @markup.strip =~ SYNTAX
|
16
|
+
@to = $1
|
17
|
+
@operator = $2
|
18
|
+
|
19
|
+
output = super
|
20
|
+
if @operator == '+=' and !context.scopes.last[@to].nil?
|
21
|
+
context.scopes.last[@to] += output.strip
|
22
|
+
elsif @operator.nil? or (@operator == '||=' and context.scopes.last[@to].nil?)
|
23
|
+
context.scopes.last[@to] = output.strip
|
24
|
+
end
|
25
|
+
|
26
|
+
else
|
27
|
+
raise SyntaxError.new("Syntax Error in 'capture' - Valid syntax: capture [var]")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
''
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/include'
|
2
|
+
|
3
|
+
module LiquidPlus
|
4
|
+
class IncludeTag < Liquid::Tag
|
5
|
+
|
6
|
+
def initialize(tag_name, markup, tokens)
|
7
|
+
@markup = markup.strip
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def render(context)
|
12
|
+
Include.render(@markup, context)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# Title: Render Partial Tag for Jekyll
|
2
|
+
# Author: Brandon Mathis
|
3
|
+
# Description: Import files on your filesystem into any blog post and render them inline.
|
4
|
+
# Note: Paths are relative to the source directory, if you import a file with yaml front matter, the yaml will be stripped out.
|
5
|
+
#
|
6
|
+
# Syntax {% render_partial path/to/file %}
|
7
|
+
#
|
8
|
+
# Example 1:
|
9
|
+
# {% render_partial about/_bio.markdown %}
|
10
|
+
#
|
11
|
+
# This will import source/about/_bio.markdown and render it inline.
|
12
|
+
# In this example I used an underscore at the beginning of the filename to prevent Jekyll
|
13
|
+
# from generating an about/bio.html (Jekyll doesn't convert files beginning with underscores)
|
14
|
+
#
|
15
|
+
# Example 2:
|
16
|
+
# {% render_partial ../README.markdown %}
|
17
|
+
#
|
18
|
+
# You can use relative pathnames, to include files outside of the source directory.
|
19
|
+
# This might be useful if you want to have a page for a project's README without having
|
20
|
+
# to duplicated the contents
|
21
|
+
#
|
22
|
+
#
|
23
|
+
|
24
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/path'
|
25
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/include'
|
26
|
+
require 'jekyll'
|
27
|
+
require 'yaml'
|
28
|
+
require 'pathname'
|
29
|
+
|
30
|
+
module Jekyll
|
31
|
+
|
32
|
+
# Create a new page class to allow partials to trigger Jekyll Page Hooks.
|
33
|
+
class ConvertiblePage
|
34
|
+
include Convertible
|
35
|
+
|
36
|
+
attr_accessor :name, :content, :site, :ext, :output, :data
|
37
|
+
|
38
|
+
def initialize(site, name, content)
|
39
|
+
@site = site
|
40
|
+
@name = name
|
41
|
+
@ext = File.extname(name)
|
42
|
+
@content = content
|
43
|
+
@data = { layout: "no_layout" } # hack
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def render(payload, info)
|
48
|
+
do_layout(payload, { no_layout: nil })
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module LiquidPlus
|
54
|
+
class RenderTag < Liquid::Tag
|
55
|
+
def initialize(tag_name, markup, tokens)
|
56
|
+
@markup = markup.strip
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_markup
|
61
|
+
# If raw, strip from the markup as not to confuse the Path syntax parsing
|
62
|
+
if @markup =~ /^(\s*raw\s)?(.+?)(\sraw\s*)?$/
|
63
|
+
@markup = $2.strip
|
64
|
+
@raw = true unless $1.nil? and $3.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Separate params from markup
|
68
|
+
@markup, @params = Include.split_params(@markup)
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_path(context)
|
72
|
+
if file = Path.parse(@markup, context)
|
73
|
+
path = Pathname.new Path.expand(file, context)
|
74
|
+
@markup = file
|
75
|
+
path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def render(context)
|
80
|
+
parse_markup
|
81
|
+
path = get_path(context)
|
82
|
+
if path and File.exist? path
|
83
|
+
|
84
|
+
Dir.chdir(File.dirname(path)) do
|
85
|
+
content = path.read
|
86
|
+
|
87
|
+
#Strip out yaml header from partials
|
88
|
+
if content =~ /\A-{3}(.+[^\A])-{3}\n(.+)/m
|
89
|
+
@local_vars = YAML.safe_load($1.strip)
|
90
|
+
content = $2.strip
|
91
|
+
end
|
92
|
+
|
93
|
+
if @raw
|
94
|
+
content
|
95
|
+
else
|
96
|
+
content = parse_params(content, context) if @params or @local_vars
|
97
|
+
|
98
|
+
p = Jekyll::ConvertiblePage.new(context.registers[:site], path, content)
|
99
|
+
payload = { 'page' => context.registers[:page], 'site' => context.registers[:site] }
|
100
|
+
p.render(payload, { registers: context.registers })
|
101
|
+
p.output.strip
|
102
|
+
end
|
103
|
+
end
|
104
|
+
elsif path
|
105
|
+
name = File.basename(path)
|
106
|
+
dir = path.to_s.sub(context.registers[:site].source + '/', '')
|
107
|
+
|
108
|
+
msg = "From #{context.registers[:page]['path']}: "
|
109
|
+
msg += "File '#{name}' not found"
|
110
|
+
msg += " in '#{dir}' directory" unless name == dir
|
111
|
+
|
112
|
+
puts msg.red
|
113
|
+
return msg
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse_params(content, context)
|
118
|
+
if @params
|
119
|
+
markup = @markup + @params
|
120
|
+
end
|
121
|
+
partial = Liquid::Template.parse(content)
|
122
|
+
|
123
|
+
context.stack do
|
124
|
+
c = context
|
125
|
+
c['render'] = Jekyll::Tags::IncludeTag.new('', markup, []).parse_params(context) if @params
|
126
|
+
c['page'] = c['page'].deep_merge(@local_vars) if @local_vars and @local_vars.keys.size > 0
|
127
|
+
content = partial.render(c)
|
128
|
+
end
|
129
|
+
content
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/var'
|
2
|
+
|
3
|
+
module LiquidPlus
|
4
|
+
class ReturnTag < Liquid::Tag
|
5
|
+
|
6
|
+
def initialize(tag_name, markup, tokens)
|
7
|
+
@markup = markup.strip
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def render(context)
|
12
|
+
if @markup = Conditional.parse(@markup, context)
|
13
|
+
Var.get_value(@markup, context)
|
14
|
+
else
|
15
|
+
''
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.join File.expand_path('../../', __FILE__), 'helpers/include'
|
2
|
+
require File.join File.expand_path('../../', __FILE__), 'tags/render'
|
3
|
+
|
4
|
+
module LiquidPlus
|
5
|
+
class WrapTag < Liquid::Block
|
6
|
+
HAS_CONTENT = /(.*?)({=\s*yield\s*})(.*)/im
|
7
|
+
|
8
|
+
def initialize(tag_name, markup, tokens)
|
9
|
+
@tag = tag_name
|
10
|
+
@markup = markup.strip
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def render(context)
|
15
|
+
if super.strip =~ HAS_CONTENT
|
16
|
+
front = $1
|
17
|
+
back = $3
|
18
|
+
|
19
|
+
partial = if @tag == 'wrap'
|
20
|
+
RenderTag.new('', @markup, []).render(context)
|
21
|
+
elsif @tag == 'wrap_include'
|
22
|
+
Include.render(@markup, context)
|
23
|
+
end
|
24
|
+
|
25
|
+
if partial
|
26
|
+
front + partial.strip + back
|
27
|
+
else
|
28
|
+
''
|
29
|
+
end
|
30
|
+
else
|
31
|
+
raise SyntaxError.new("Syntax Error in '#{@tag}' - Valid syntax: {% wrap_include template/file.html %}\n[<div>]{= yield }[</div>]\n{% end#{@tag} %}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|