jekyll-liquid-plus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+