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.
@@ -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
+