agilitic-liquid 2.0.1
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.
- data/CHANGELOG +44 -0
- data/History.txt +44 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +34 -0
- data/README.txt +38 -0
- data/Rakefile +45 -0
- data/lib/extras/liquid_view.rb +51 -0
- data/lib/liquid.rb +68 -0
- data/lib/liquid/block.rb +101 -0
- data/lib/liquid/condition.rb +120 -0
- data/lib/liquid/context.rb +221 -0
- data/lib/liquid/document.rb +17 -0
- data/lib/liquid/drop.rb +51 -0
- data/lib/liquid/errors.rb +11 -0
- data/lib/liquid/extensions.rb +56 -0
- data/lib/liquid/file_system.rb +62 -0
- data/lib/liquid/htmltags.rb +74 -0
- data/lib/liquid/module_ex.rb +62 -0
- data/lib/liquid/standardfilters.rb +214 -0
- data/lib/liquid/strainer.rb +51 -0
- data/lib/liquid/tag.rb +26 -0
- data/lib/liquid/tags/assign.rb +33 -0
- data/lib/liquid/tags/capture.rb +35 -0
- data/lib/liquid/tags/case.rb +83 -0
- data/lib/liquid/tags/comment.rb +9 -0
- data/lib/liquid/tags/cycle.rb +59 -0
- data/lib/liquid/tags/for.rb +136 -0
- data/lib/liquid/tags/if.rb +79 -0
- data/lib/liquid/tags/ifchanged.rb +20 -0
- data/lib/liquid/tags/include.rb +55 -0
- data/lib/liquid/tags/unless.rb +33 -0
- data/lib/liquid/template.rb +147 -0
- data/lib/liquid/variable.rb +54 -0
- metadata +90 -0
data/lib/liquid/tag.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
class Tag
|
4
|
+
attr_accessor :nodelist
|
5
|
+
|
6
|
+
def initialize(tag_name, markup, tokens)
|
7
|
+
@tag_name = tag_name
|
8
|
+
@markup = markup
|
9
|
+
parse(tokens)
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse(tokens)
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
self.class.name.downcase
|
17
|
+
end
|
18
|
+
|
19
|
+
def render(context)
|
20
|
+
''
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
# Assign sets a variable in your template.
|
4
|
+
#
|
5
|
+
# {% assign foo = 'monkey' %}
|
6
|
+
#
|
7
|
+
# You can then use the variable later in the page.
|
8
|
+
#
|
9
|
+
# {{ monkey }}
|
10
|
+
#
|
11
|
+
class Assign < Tag
|
12
|
+
Syntax = /(#{VariableSignature}+)\s*=\s*(#{QuotedFragment}+)/
|
13
|
+
|
14
|
+
def initialize(tag_name, markup, tokens)
|
15
|
+
if markup =~ Syntax
|
16
|
+
@to = $1
|
17
|
+
@from = $2
|
18
|
+
else
|
19
|
+
raise SyntaxError.new("Syntax Error in 'assign' - Valid syntax: assign [var] = [source]")
|
20
|
+
end
|
21
|
+
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def render(context)
|
26
|
+
context.scopes.last[@to.to_s] = context[@from]
|
27
|
+
''
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
Template.register_tag('assign', Assign)
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
# Capture stores the result of a block into a variable without rendering it inplace.
|
4
|
+
#
|
5
|
+
# {% capture heading %}
|
6
|
+
# Monkeys!
|
7
|
+
# {% endcapture %}
|
8
|
+
# ...
|
9
|
+
# <h1>{{ monkeys }}</h1>
|
10
|
+
#
|
11
|
+
# Capture is useful for saving content for use later in your template, such as
|
12
|
+
# in a sidebar or footer.
|
13
|
+
#
|
14
|
+
class Capture < Block
|
15
|
+
Syntax = /(\w+)/
|
16
|
+
|
17
|
+
def initialize(tag_name, markup, tokens)
|
18
|
+
if markup =~ Syntax
|
19
|
+
@to = $1
|
20
|
+
else
|
21
|
+
raise SyntaxError.new("Syntax Error in 'capture' - Valid syntax: capture [var]")
|
22
|
+
end
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def render(context)
|
28
|
+
output = super
|
29
|
+
context[@to] = output.join
|
30
|
+
''
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Template.register_tag('capture', Capture)
|
35
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Liquid
|
2
|
+
class Case < Block
|
3
|
+
Syntax = /(#{QuotedFragment})/
|
4
|
+
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/
|
5
|
+
|
6
|
+
def initialize(tag_name, markup, tokens)
|
7
|
+
@blocks = []
|
8
|
+
|
9
|
+
if markup =~ Syntax
|
10
|
+
@left = $1
|
11
|
+
else
|
12
|
+
raise SyntaxError.new("Syntax Error in tag 'case' - Valid syntax: case [condition]")
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def unknown_tag(tag, markup, tokens)
|
19
|
+
@nodelist = []
|
20
|
+
case tag
|
21
|
+
when 'when'
|
22
|
+
record_when_condition(markup)
|
23
|
+
when 'else'
|
24
|
+
record_else_condition(markup)
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def render(context)
|
31
|
+
context.stack do
|
32
|
+
execute_else_block = true
|
33
|
+
|
34
|
+
@blocks.inject([]) do |output, block|
|
35
|
+
|
36
|
+
if block.else?
|
37
|
+
|
38
|
+
return render_all(block.attachment, context) if execute_else_block
|
39
|
+
|
40
|
+
elsif block.evaluate(context)
|
41
|
+
|
42
|
+
execute_else_block = false
|
43
|
+
output += render_all(block.attachment, context)
|
44
|
+
end
|
45
|
+
|
46
|
+
output
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def record_when_condition(markup)
|
54
|
+
while markup
|
55
|
+
# Create a new nodelist and assign it to the new block
|
56
|
+
if not markup =~ WhenSyntax
|
57
|
+
raise SyntaxError.new("Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %} ")
|
58
|
+
end
|
59
|
+
|
60
|
+
markup = $2
|
61
|
+
|
62
|
+
block = Condition.new(@left, '==', $1)
|
63
|
+
block.attach(@nodelist)
|
64
|
+
@blocks.push(block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def record_else_condition(markup)
|
69
|
+
|
70
|
+
if not markup.strip.empty?
|
71
|
+
raise SyntaxError.new("Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) ")
|
72
|
+
end
|
73
|
+
|
74
|
+
block = ElseCondition.new
|
75
|
+
block.attach(@nodelist)
|
76
|
+
@blocks << block
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
Template.register_tag('case', Case)
|
83
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
# Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
|
4
|
+
#
|
5
|
+
# {% for item in items %}
|
6
|
+
# <div class="{% cycle 'red', 'green', 'blue' %}"> {{ item }} </div>
|
7
|
+
# {% end %}
|
8
|
+
#
|
9
|
+
# <div class="red"> Item one </div>
|
10
|
+
# <div class="green"> Item two </div>
|
11
|
+
# <div class="blue"> Item three </div>
|
12
|
+
# <div class="red"> Item four </div>
|
13
|
+
# <div class="green"> Item five</div>
|
14
|
+
#
|
15
|
+
class Cycle < Tag
|
16
|
+
SimpleSyntax = /^#{QuotedFragment}+/
|
17
|
+
NamedSyntax = /^(#{QuotedFragment})\s*\:\s*(.*)/
|
18
|
+
|
19
|
+
def initialize(tag_name, markup, tokens)
|
20
|
+
case markup
|
21
|
+
when NamedSyntax
|
22
|
+
@variables = variables_from_string($2)
|
23
|
+
@name = $1
|
24
|
+
when SimpleSyntax
|
25
|
+
@variables = variables_from_string(markup)
|
26
|
+
@name = "'#{@variables.to_s}'"
|
27
|
+
else
|
28
|
+
raise SyntaxError.new("Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]")
|
29
|
+
end
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def render(context)
|
34
|
+
context.registers[:cycle] ||= Hash.new(0)
|
35
|
+
|
36
|
+
context.stack do
|
37
|
+
key = context[@name]
|
38
|
+
iteration = context.registers[:cycle][key]
|
39
|
+
result = context[@variables[iteration]]
|
40
|
+
iteration += 1
|
41
|
+
iteration = 0 if iteration >= @variables.size
|
42
|
+
context.registers[:cycle][key] = iteration
|
43
|
+
result
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def variables_from_string(markup)
|
50
|
+
markup.split(',').collect do |var|
|
51
|
+
var =~ /\s*(#{QuotedFragment})\s*/
|
52
|
+
$1 ? $1 : nil
|
53
|
+
end.compact
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
Template.register_tag('cycle', Cycle)
|
59
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
# "For" iterates over an array or collection.
|
4
|
+
# Several useful variables are available to you within the loop.
|
5
|
+
#
|
6
|
+
# == Basic usage:
|
7
|
+
# {% for item in collection %}
|
8
|
+
# {{ forloop.index }}: {{ item.name }}
|
9
|
+
# {% endfor %}
|
10
|
+
#
|
11
|
+
# == Advanced usage:
|
12
|
+
# {% for item in collection %}
|
13
|
+
# <div {% if forloop.first %}class="first"{% endif %}>
|
14
|
+
# Item {{ forloop.index }}: {{ item.name }}
|
15
|
+
# </div>
|
16
|
+
# {% endfor %}
|
17
|
+
#
|
18
|
+
# You can also define a limit and offset much like SQL. Remember
|
19
|
+
# that offset starts at 0 for the first item.
|
20
|
+
#
|
21
|
+
# {% for item in collection limit:5 offset:10 %}
|
22
|
+
# {{ item.name }}
|
23
|
+
# {% end %}
|
24
|
+
#
|
25
|
+
# To reverse the for loop simply use {% for item in collection reversed %}
|
26
|
+
#
|
27
|
+
# == Available variables:
|
28
|
+
#
|
29
|
+
# forloop.name:: 'item-collection'
|
30
|
+
# forloop.length:: Length of the loop
|
31
|
+
# forloop.index:: The current item's position in the collection;
|
32
|
+
# forloop.index starts at 1.
|
33
|
+
# This is helpful for non-programmers who start believe
|
34
|
+
# the first item in an array is 1, not 0.
|
35
|
+
# forloop.index0:: The current item's position in the collection
|
36
|
+
# where the first item is 0
|
37
|
+
# forloop.rindex:: Number of items remaining in the loop
|
38
|
+
# (length - index) where 1 is the last item.
|
39
|
+
# forloop.rindex0:: Number of items remaining in the loop
|
40
|
+
# where 0 is the last item.
|
41
|
+
# forloop.first:: Returns true if the item is the first item.
|
42
|
+
# forloop.last:: Returns true if the item is the last item.
|
43
|
+
#
|
44
|
+
class For < Block
|
45
|
+
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/
|
46
|
+
|
47
|
+
def initialize(tag_name, markup, tokens)
|
48
|
+
if markup =~ Syntax
|
49
|
+
@variable_name = $1
|
50
|
+
@collection_name = $2
|
51
|
+
@name = "#{$1}-#{$2}"
|
52
|
+
@reversed = $3
|
53
|
+
@attributes = {}
|
54
|
+
markup.scan(TagAttributes) do |key, value|
|
55
|
+
@attributes[key] = value
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise SyntaxError.new("Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]")
|
59
|
+
end
|
60
|
+
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
def render(context)
|
65
|
+
context.registers[:for] ||= Hash.new(0)
|
66
|
+
|
67
|
+
collection = context[@collection_name]
|
68
|
+
collection = collection.to_a if collection.is_a?(Range)
|
69
|
+
|
70
|
+
return '' unless collection.respond_to?(:each)
|
71
|
+
|
72
|
+
from = if @attributes['offset'] == 'continue'
|
73
|
+
context.registers[:for][@name].to_i
|
74
|
+
else
|
75
|
+
context[@attributes['offset']].to_i
|
76
|
+
end
|
77
|
+
|
78
|
+
limit = context[@attributes['limit']]
|
79
|
+
to = limit ? limit.to_i + from : nil
|
80
|
+
|
81
|
+
|
82
|
+
segment = slice_collection_using_each(collection, from, to)
|
83
|
+
|
84
|
+
return '' if segment.empty?
|
85
|
+
|
86
|
+
segment.reverse! if @reversed
|
87
|
+
|
88
|
+
result = []
|
89
|
+
|
90
|
+
length = segment.length
|
91
|
+
|
92
|
+
# Store our progress through the collection for the continue flag
|
93
|
+
context.registers[:for][@name] = from + segment.length
|
94
|
+
|
95
|
+
context.stack do
|
96
|
+
segment.each_with_index do |item, index|
|
97
|
+
context[@variable_name] = item
|
98
|
+
context['forloop'] = {
|
99
|
+
'name' => @name,
|
100
|
+
'length' => length,
|
101
|
+
'index' => index + 1,
|
102
|
+
'index0' => index,
|
103
|
+
'rindex' => length - index,
|
104
|
+
'rindex0' => length - index -1,
|
105
|
+
'first' => (index == 0),
|
106
|
+
'last' => (index == length - 1) }
|
107
|
+
|
108
|
+
result << render_all(@nodelist, context)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
result
|
112
|
+
end
|
113
|
+
|
114
|
+
def slice_collection_using_each(collection, from, to)
|
115
|
+
segments = []
|
116
|
+
index = 0
|
117
|
+
yielded = 0
|
118
|
+
collection.each do |item|
|
119
|
+
|
120
|
+
if to && to <= index
|
121
|
+
break
|
122
|
+
end
|
123
|
+
|
124
|
+
if from <= index
|
125
|
+
segments << item
|
126
|
+
end
|
127
|
+
|
128
|
+
index += 1
|
129
|
+
end
|
130
|
+
|
131
|
+
segments
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
Template.register_tag('for', For)
|
136
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
# If is the conditional block
|
4
|
+
#
|
5
|
+
# {% if user.admin %}
|
6
|
+
# Admin user!
|
7
|
+
# {% else %}
|
8
|
+
# Not admin user
|
9
|
+
# {% endif %}
|
10
|
+
#
|
11
|
+
# There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
|
12
|
+
#
|
13
|
+
#
|
14
|
+
class If < Block
|
15
|
+
SyntaxHelp = "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
16
|
+
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/
|
17
|
+
|
18
|
+
def initialize(tag_name, markup, tokens)
|
19
|
+
|
20
|
+
@blocks = []
|
21
|
+
|
22
|
+
push_block('if', markup)
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def unknown_tag(tag, markup, tokens)
|
28
|
+
if ['elsif', 'else'].include?(tag)
|
29
|
+
push_block(tag, markup)
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def render(context)
|
36
|
+
context.stack do
|
37
|
+
@blocks.each do |block|
|
38
|
+
if block.evaluate(context)
|
39
|
+
return render_all(block.attachment, context)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
''
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def push_block(tag, markup)
|
49
|
+
block = if tag == 'else'
|
50
|
+
ElseCondition.new
|
51
|
+
else
|
52
|
+
|
53
|
+
expressions = markup.split(/\b(and|or)\b/).reverse
|
54
|
+
raise(SyntaxError, SyntaxHelp) unless expressions.shift =~ Syntax
|
55
|
+
|
56
|
+
condition = Condition.new($1, $2, $3)
|
57
|
+
|
58
|
+
while not expressions.empty?
|
59
|
+
operator = expressions.shift
|
60
|
+
|
61
|
+
raise(SyntaxError, SyntaxHelp) unless expressions.shift.to_s =~ Syntax
|
62
|
+
|
63
|
+
new_condition = Condition.new($1, $2, $3)
|
64
|
+
new_condition.send(operator.to_sym, condition)
|
65
|
+
condition = new_condition
|
66
|
+
end
|
67
|
+
|
68
|
+
condition
|
69
|
+
end
|
70
|
+
|
71
|
+
@blocks.push(block)
|
72
|
+
@nodelist = block.attach(Array.new)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
Template.register_tag('if', If)
|
79
|
+
end
|