merb_comatose 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +4 -0
- data/Rakefile +52 -0
- data/TODO +5 -0
- data/lib/class_options.rb +36 -0
- data/lib/comatose/comatose_drop.rb +64 -0
- data/lib/comatose/configuration.rb +69 -0
- data/lib/comatose/page_wrapper.rb +119 -0
- data/lib/comatose/processing_context.rb +69 -0
- data/lib/comatose/version.rb +4 -0
- data/lib/liquid/block.rb +96 -0
- data/lib/liquid/context.rb +190 -0
- data/lib/liquid/document.rb +17 -0
- data/lib/liquid/drop.rb +48 -0
- data/lib/liquid/errors.rb +7 -0
- data/lib/liquid/extensions.rb +53 -0
- data/lib/liquid/file_system.rb +62 -0
- data/lib/liquid/htmltags.rb +64 -0
- data/lib/liquid/standardfilters.rb +111 -0
- data/lib/liquid/standardtags.rb +399 -0
- data/lib/liquid/strainer.rb +42 -0
- data/lib/liquid/tag.rb +25 -0
- data/lib/liquid/template.rb +88 -0
- data/lib/liquid/variable.rb +39 -0
- data/lib/liquid.rb +52 -0
- data/lib/merb_comatose/merbtasks.rb +6 -0
- data/lib/merb_comatose.rb +30 -0
- data/lib/redcloth.rb +1129 -0
- data/lib/support/class_options.rb +36 -0
- data/lib/text_filters/markdown.rb +14 -0
- data/lib/text_filters/markdown_smartypants.rb +15 -0
- data/lib/text_filters/none.rb +8 -0
- data/lib/text_filters/rdoc.rb +13 -0
- data/lib/text_filters/simple.rb +8 -0
- data/lib/text_filters/textile.rb +15 -0
- data/lib/text_filters.rb +138 -0
- data/spec/merb_comatose_spec.rb +7 -0
- data/spec/spec_helper.rb +1 -0
- metadata +97 -0
@@ -0,0 +1,399 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
class Assign < Tag
|
4
|
+
Syntax = /(\w+)\s*=\s*(#{AllowedVariableCharacters}+)/
|
5
|
+
|
6
|
+
def initialize(markup, tokens)
|
7
|
+
if markup =~ Syntax
|
8
|
+
@to = $1
|
9
|
+
@from = $2
|
10
|
+
else
|
11
|
+
raise SyntaxError.new("Syntax Error in 'assign' - Valid syntax: assign [var] = [source]")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def render(context)
|
16
|
+
context[@to] = context[@from]
|
17
|
+
''
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class Capture < Block
|
23
|
+
Syntax = /(\w+)/
|
24
|
+
|
25
|
+
def initialize(markup, tokens)
|
26
|
+
if markup =~ Syntax
|
27
|
+
@to = $1
|
28
|
+
super
|
29
|
+
else
|
30
|
+
raise SyntaxError.new("Syntax Error in 'capture' - Valid syntax: capture [var]")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def render(context)
|
35
|
+
output = super
|
36
|
+
context[@to] = output.to_s
|
37
|
+
''
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Cycle < Tag
|
42
|
+
SimpleSyntax = /#{QuotedFragment}/
|
43
|
+
NamedSyntax = /(#{QuotedFragment})\s*\:\s*(.*)/
|
44
|
+
|
45
|
+
def initialize(markup, tokens)
|
46
|
+
case markup
|
47
|
+
when NamedSyntax
|
48
|
+
@variables = variables_from_string($2)
|
49
|
+
@name = $1
|
50
|
+
when SimpleSyntax
|
51
|
+
@variables = variables_from_string(markup)
|
52
|
+
@name = "'#{@variables.to_s}'"
|
53
|
+
else
|
54
|
+
raise SyntaxError.new("Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]")
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
def render(context)
|
60
|
+
context.registers[:cycle] ||= Hash.new(0)
|
61
|
+
|
62
|
+
context.stack do
|
63
|
+
key = context[@name]
|
64
|
+
iteration = context.registers[:cycle][key]
|
65
|
+
result = context[@variables[iteration]]
|
66
|
+
iteration += 1
|
67
|
+
iteration = 0 if iteration >= @variables.size
|
68
|
+
context.registers[:cycle][key] = iteration
|
69
|
+
result
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def variables_from_string(markup)
|
76
|
+
markup.split(',').collect do |var|
|
77
|
+
var =~ /\s*(#{QuotedFragment})\s*/
|
78
|
+
$1 ? $1 : nil
|
79
|
+
end.compact
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
class Comment < Block
|
85
|
+
def render(context)
|
86
|
+
''
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class For < Block
|
91
|
+
Syntax = /(\w+)\s+in\s+(#{AllowedVariableCharacters}+)/
|
92
|
+
|
93
|
+
def initialize(markup, tokens)
|
94
|
+
super
|
95
|
+
|
96
|
+
if markup =~ Syntax
|
97
|
+
@variable_name = $1
|
98
|
+
@collection_name = $2
|
99
|
+
@name = "#{$1}-#{$2}"
|
100
|
+
@attributes = {}
|
101
|
+
markup.scan(TagAttributes) do |key, value|
|
102
|
+
@attributes[key] = value
|
103
|
+
end
|
104
|
+
else
|
105
|
+
raise SyntaxError.new("Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def render(context)
|
110
|
+
context.registers[:for] ||= Hash.new(0)
|
111
|
+
|
112
|
+
collection = context[@collection_name]
|
113
|
+
|
114
|
+
return '' if collection.nil? or collection.empty?
|
115
|
+
|
116
|
+
range = (0..collection.length)
|
117
|
+
|
118
|
+
if @attributes['limit'] or @attributes['offset']
|
119
|
+
|
120
|
+
|
121
|
+
offset = 0
|
122
|
+
if @attributes['offset'] == 'continue'
|
123
|
+
offset = context.registers[:for][@name]
|
124
|
+
else
|
125
|
+
offset = context[@attributes['offset']] || 0
|
126
|
+
end
|
127
|
+
|
128
|
+
limit = context[@attributes['limit']]
|
129
|
+
|
130
|
+
range_end = limit ? offset + limit : collection.length
|
131
|
+
|
132
|
+
range = (offset..range_end-1)
|
133
|
+
|
134
|
+
# Save the range end in the registers so that future calls to
|
135
|
+
# offset:continue have something to pick up
|
136
|
+
context.registers[:for][@name] = range_end
|
137
|
+
end
|
138
|
+
|
139
|
+
result = []
|
140
|
+
segment = collection[range]
|
141
|
+
return '' if segment.nil?
|
142
|
+
|
143
|
+
context.stack do
|
144
|
+
length = segment.length
|
145
|
+
|
146
|
+
segment.each_with_index do |item, index|
|
147
|
+
context[@variable_name] = item
|
148
|
+
context['forloop'] = {
|
149
|
+
'name' => @name,
|
150
|
+
'length' => length,
|
151
|
+
'index' => index + 1,
|
152
|
+
'index0' => index,
|
153
|
+
'rindex' => length - index,
|
154
|
+
'rindex0' => length - index -1,
|
155
|
+
'first' => (index == 0),
|
156
|
+
'last' => (index == length - 1) }
|
157
|
+
|
158
|
+
result << render_all(@nodelist, context)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Store position of last element we rendered. This allows us to do
|
163
|
+
|
164
|
+
result
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
class DecisionBlock < Block
|
170
|
+
def equal_variables(right, left)
|
171
|
+
if left.is_a?(Symbol)
|
172
|
+
if right.respond_to?(left.to_s)
|
173
|
+
return right.send(left.to_s)
|
174
|
+
else
|
175
|
+
raise ArgumentError.new("Error in tag '#{name}' - Cannot call method #{left} on type #{right.class}}")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
if right.is_a?(Symbol)
|
180
|
+
if left.respond_to?(right.to_s)
|
181
|
+
return left.send(right.to_s)
|
182
|
+
else
|
183
|
+
raise ArgumentError.new("Error in tag '#{name}' - Cannot call method #{right} on type #{left.class}}")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
left == right
|
188
|
+
end
|
189
|
+
|
190
|
+
def interpret_condition(left, right, op, context)
|
191
|
+
|
192
|
+
# If the operator is empty this means that the decision statement is just
|
193
|
+
# a single variable. We can just poll this variable from the context and
|
194
|
+
# return this as the result.
|
195
|
+
return context[left] if op == nil
|
196
|
+
|
197
|
+
left, right = context[left], context[right]
|
198
|
+
|
199
|
+
operation = case op
|
200
|
+
when '==' then return equal_variables(left, right)
|
201
|
+
when '!=' then return !equal_variables(left, right)
|
202
|
+
when '>' then :>
|
203
|
+
when '<' then :<
|
204
|
+
when '>=' then :>=
|
205
|
+
when '<=' then :<=
|
206
|
+
else
|
207
|
+
raise ArgumentError.new("Error in tag '#{name}' - Unknown operator #{op}")
|
208
|
+
end
|
209
|
+
|
210
|
+
if left.respond_to?(operation) and right.respond_to?(operation)
|
211
|
+
left.send(operation, right)
|
212
|
+
else
|
213
|
+
nil
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
class Case < DecisionBlock
|
220
|
+
Syntax = /(#{QuotedFragment})/
|
221
|
+
WhenSyntax = /(#{QuotedFragment})/
|
222
|
+
|
223
|
+
def initialize(markup, tokens)
|
224
|
+
@nodelists = []
|
225
|
+
@else_nodelist = []
|
226
|
+
|
227
|
+
super
|
228
|
+
|
229
|
+
if markup =~ Syntax
|
230
|
+
@left = $1
|
231
|
+
else
|
232
|
+
raise SyntaxError.new("Syntax Error in tag 'case' - Valid syntax: case [condition]")
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def end_tag
|
237
|
+
push_nodelist
|
238
|
+
end
|
239
|
+
|
240
|
+
def unknown_tag(tag, params, tokens)
|
241
|
+
case tag
|
242
|
+
when 'when'
|
243
|
+
if params =~ WhenSyntax
|
244
|
+
push_nodelist
|
245
|
+
@right = $1
|
246
|
+
@nodelist = []
|
247
|
+
else
|
248
|
+
raise SyntaxError.new("Syntax Error in tag 'case' - Valid when condition: when [condition] ")
|
249
|
+
end
|
250
|
+
when 'else'
|
251
|
+
push_nodelist
|
252
|
+
@right = nil
|
253
|
+
@else_nodelist = @nodelist = []
|
254
|
+
else
|
255
|
+
super
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def push_nodelist
|
260
|
+
if @right
|
261
|
+
# only push the nodelist if there was actually a when condition stated before.
|
262
|
+
# we discard all tokens between the case and the first when condition this way...
|
263
|
+
@nodelists << [@right, @nodelist]
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def render(context)
|
268
|
+
output = []
|
269
|
+
run_else_block = true
|
270
|
+
|
271
|
+
@nodelists.each do |right, nodelist|
|
272
|
+
if equal_variables(context[@left], context[right])
|
273
|
+
run_else_block = false
|
274
|
+
context.stack do
|
275
|
+
output += render_all(nodelist, context)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
if run_else_block
|
281
|
+
context.stack do
|
282
|
+
output += render_all(@else_nodelist, context)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
output.to_s
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
class If < DecisionBlock
|
291
|
+
Syntax = /(#{QuotedFragment})\s*([=!<>]+)?\s*(#{QuotedFragment})?/
|
292
|
+
|
293
|
+
def initialize(markup, tokens)
|
294
|
+
@nodelist_true = @nodelist = []
|
295
|
+
@nodelist_false = []
|
296
|
+
|
297
|
+
super
|
298
|
+
|
299
|
+
if markup =~ Syntax
|
300
|
+
@left = $1
|
301
|
+
@operator = $2
|
302
|
+
@right = $3
|
303
|
+
else
|
304
|
+
raise SyntaxError.new("Syntax Error in tag 'if' - Valid syntax: if [condition]")
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
def unknown_tag(tag, params, tokens)
|
310
|
+
if tag == 'else'
|
311
|
+
@nodelist = @nodelist_false = []
|
312
|
+
else
|
313
|
+
super
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def render(context)
|
318
|
+
context.stack do
|
319
|
+
if interpret_condition(@left, @right, @operator, context)
|
320
|
+
render_all(@nodelist_true, context)
|
321
|
+
else
|
322
|
+
render_all(@nodelist_false, context)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
class Unless < If
|
329
|
+
def render(context)
|
330
|
+
context.stack do
|
331
|
+
if interpret_condition(@left, @right, @operator, context)
|
332
|
+
render_all(@nodelist_false, context)
|
333
|
+
else
|
334
|
+
render_all(@nodelist_true, context)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class Include < Tag
|
341
|
+
Syntax = /("[^"]+"|'[^']+')(\s+(with|for)\s+(#{QuotedFragment}+))?/
|
342
|
+
|
343
|
+
def initialize(markup, tokens)
|
344
|
+
if markup =~ Syntax
|
345
|
+
@template_name = $1[1...-1]
|
346
|
+
if $2
|
347
|
+
@collection = ($3 == "for")
|
348
|
+
@variable = $4
|
349
|
+
end
|
350
|
+
@attributes = {}
|
351
|
+
markup.scan(TagAttributes) do |key, value|
|
352
|
+
@attributes[key] = value
|
353
|
+
end
|
354
|
+
else
|
355
|
+
raise SyntaxError.new("Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]")
|
356
|
+
end
|
357
|
+
|
358
|
+
super
|
359
|
+
end
|
360
|
+
|
361
|
+
def parse(tokens)
|
362
|
+
source = Liquid::Template.file_system.read_template_file(@template_name)
|
363
|
+
tokens = Liquid::Template.tokenize(source)
|
364
|
+
@document = Document.new(tokens)
|
365
|
+
end
|
366
|
+
|
367
|
+
def render(context)
|
368
|
+
result = ''
|
369
|
+
variable = context[@variable]
|
370
|
+
context.stack do
|
371
|
+
@attributes.each do |key, value|
|
372
|
+
context[key] = context[value]
|
373
|
+
end
|
374
|
+
if @collection
|
375
|
+
variable.each do |item|
|
376
|
+
context[@template_name] = item
|
377
|
+
result << @document.render(context).to_s
|
378
|
+
end
|
379
|
+
else
|
380
|
+
if @variable
|
381
|
+
context[@template_name] = variable
|
382
|
+
end
|
383
|
+
result << @document.render(context).to_s
|
384
|
+
end
|
385
|
+
end
|
386
|
+
result
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
Template.register_tag('assign', Assign)
|
391
|
+
Template.register_tag('capture', Capture)
|
392
|
+
Template.register_tag('comment', Comment)
|
393
|
+
Template.register_tag('for', For)
|
394
|
+
Template.register_tag('if', If)
|
395
|
+
Template.register_tag('unless', Unless)
|
396
|
+
Template.register_tag('case', Case)
|
397
|
+
Template.register_tag('cycle', Cycle)
|
398
|
+
Template.register_tag('include', Include)
|
399
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
# Strainer is the parent class for the filters system.
|
4
|
+
# New filters are mixed into the strainer class which is then instanciated for each liquid template render run.
|
5
|
+
#
|
6
|
+
# One of the strainer's responsibilities is to keep malicious method calls out
|
7
|
+
class Strainer
|
8
|
+
@@required_methods = ["__send__", "__id__", "respond_to?", "extend", "methods"]
|
9
|
+
|
10
|
+
@@filters = []
|
11
|
+
|
12
|
+
def initialize(context)
|
13
|
+
@context = context
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.global_filter(filter)
|
17
|
+
raise StandardError, "Passed filter is not a module" unless filter.is_a?(Module)
|
18
|
+
@@filters << filter
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create(context)
|
22
|
+
strainer = Strainer.new(context)
|
23
|
+
@@filters.each { |m| strainer.extend(m) }
|
24
|
+
strainer
|
25
|
+
end
|
26
|
+
|
27
|
+
def respond_to?(method)
|
28
|
+
method_name = method.to_s
|
29
|
+
return false if method_name =~ /^__/
|
30
|
+
return false if @@required_methods.include?(method_name)
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
# remove all standard methods from the bucket so circumvent security
|
35
|
+
# problems
|
36
|
+
instance_methods.each do |m|
|
37
|
+
unless @@required_methods.include?(m)
|
38
|
+
undef_method m
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/liquid/tag.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
class Tag
|
4
|
+
attr_accessor :nodelist
|
5
|
+
|
6
|
+
def initialize(markup, tokens)
|
7
|
+
@markup = markup
|
8
|
+
parse(tokens)
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(tokens)
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
self.class.name.downcase
|
16
|
+
end
|
17
|
+
|
18
|
+
def render(context)
|
19
|
+
''
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
# Templates are central to liquid.
|
4
|
+
# Interpretating templates is a two step process. First you compile the
|
5
|
+
# source code you got. During compile time some extensive error checking is performed.
|
6
|
+
# your code should expect to get some SyntaxErrors.
|
7
|
+
#
|
8
|
+
# After you have a compiled template you can then <tt>render</tt> it.
|
9
|
+
# You can use a compiled template over and over again and keep it cached.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# template = Liquid::Template.parse(source)
|
14
|
+
# template.render('user_name' => 'bob')
|
15
|
+
#
|
16
|
+
class Template
|
17
|
+
attr_accessor :root
|
18
|
+
@@file_system = BlankFileSystem.new
|
19
|
+
|
20
|
+
def self.file_system
|
21
|
+
@@file_system
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.file_system=(obj)
|
25
|
+
@@file_system = obj
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.register_tag(name, klass)
|
29
|
+
tags[name.to_s] = klass
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.tags
|
33
|
+
@tags ||= {}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Pass a module with filter methods which should be available
|
37
|
+
# to all liquid views. Good for registering the standard library
|
38
|
+
def self.register_filter(mod)
|
39
|
+
Strainer.global_filter(mod)
|
40
|
+
end
|
41
|
+
|
42
|
+
# creates a new <tt>Template</tt> object from liquid source code
|
43
|
+
def self.parse(source)
|
44
|
+
self.new(tokenize(source))
|
45
|
+
end
|
46
|
+
|
47
|
+
# Uses the <tt>Liquid::TokenizationRegexp</tt> regexp to tokenize the passed source
|
48
|
+
def self.tokenize(source)
|
49
|
+
return [] if source.to_s.empty?
|
50
|
+
tokens = source.split(TokenizationRegexp)
|
51
|
+
|
52
|
+
# removes the rogue empty element at the beginning of the array
|
53
|
+
tokens.shift if tokens[0] and tokens[0].empty?
|
54
|
+
|
55
|
+
tokens
|
56
|
+
end
|
57
|
+
|
58
|
+
# creates a new <tt>Template</tt> from an array of tokens. Use <tt>Template.parse</tt> instead
|
59
|
+
def initialize(tokens = [])
|
60
|
+
@root = Document.new(tokens)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Render takes a hash with local variables.
|
64
|
+
#
|
65
|
+
# if you use the same filters over and over again consider registering them globally
|
66
|
+
# with <tt>Template.register_filter</tt>
|
67
|
+
#
|
68
|
+
# Following options can be passed:
|
69
|
+
#
|
70
|
+
# * <tt>filters</tt> : array with local filters
|
71
|
+
# * <tt>registers</tt> : hash with register variables. Those can be accessed from
|
72
|
+
# filters and tags and might be useful to integrate liquid more with its host application
|
73
|
+
#
|
74
|
+
def render(assigns = {}, options = nil)
|
75
|
+
options = { :filters => options } unless options.is_a?(Hash)
|
76
|
+
context = Context.new(assigns, options[:registers])
|
77
|
+
|
78
|
+
# Apply all filters
|
79
|
+
[options[:filters]].flatten.each do |filter|
|
80
|
+
context.add_filters(filter)
|
81
|
+
end
|
82
|
+
|
83
|
+
# render the nodelist.
|
84
|
+
# for performance reasons we get a array back here. to_s will make a string out of it
|
85
|
+
@root.render(context).to_s
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
# Hols variables. Variables are only loaded "just in time"
|
4
|
+
# they are not evaluated as part of the render stage
|
5
|
+
class Variable
|
6
|
+
attr_accessor :filters, :name
|
7
|
+
|
8
|
+
def initialize(markup)
|
9
|
+
@markup = markup
|
10
|
+
@name = markup.match(/\s*(#{QuotedFragment})/)[1]
|
11
|
+
if markup.match(/#{FilterSperator}\s*(.*)/)
|
12
|
+
filters = Regexp.last_match(1).split(/#{FilterSperator}/)
|
13
|
+
|
14
|
+
@filters = filters.collect do |f|
|
15
|
+
filtername = f.match(/\s*(\w+)/)[1]
|
16
|
+
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/).flatten
|
17
|
+
[filtername.to_sym, filterargs]
|
18
|
+
end
|
19
|
+
else
|
20
|
+
@filters = []
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def render(context)
|
25
|
+
output = context[@name]
|
26
|
+
@filters.inject(output) do |output, filter|
|
27
|
+
filterargs = filter[1].to_a.collect do |a|
|
28
|
+
context[a]
|
29
|
+
end
|
30
|
+
begin
|
31
|
+
output = context.invoke(filter[0], output, *filterargs)
|
32
|
+
rescue FilterNotFound
|
33
|
+
raise FilterNotFound, "Error - filter '#{filter[0]}' in '#{@markup.strip}' could not be found."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
output
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/liquid.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Copyright (c) 2005 Tobias Luetke
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
23
|
+
|
24
|
+
module Liquid
|
25
|
+
FilterSperator = /\|/
|
26
|
+
ArgumentSeparator = ','
|
27
|
+
FilterArgumentSeparator = ':'
|
28
|
+
VariableAttributeSeparator = '.'
|
29
|
+
TagStart = /\{\%/
|
30
|
+
TagEnd = /\%\}/
|
31
|
+
VariableStart = /\{\{/
|
32
|
+
VariableEnd = /\}\}/
|
33
|
+
AllowedVariableCharacters = /[a-zA-Z_.-]/
|
34
|
+
QuotedFragment = /"[^"]+"|'[^']+'|[^\s,|]+/
|
35
|
+
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
|
36
|
+
TokenizationRegexp = /(#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableEnd})/
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'liquid/drop'
|
40
|
+
require 'liquid/extensions'
|
41
|
+
require 'liquid/errors'
|
42
|
+
require 'liquid/strainer'
|
43
|
+
require 'liquid/context'
|
44
|
+
require 'liquid/tag'
|
45
|
+
require 'liquid/block'
|
46
|
+
require 'liquid/document'
|
47
|
+
require 'liquid/variable'
|
48
|
+
require 'liquid/file_system'
|
49
|
+
require 'liquid/template'
|
50
|
+
require 'liquid/standardtags'
|
51
|
+
require 'liquid/htmltags'
|
52
|
+
require 'liquid/standardfilters'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'redcloth' unless defined?(RedCloth)
|
2
|
+
require 'liquid' unless defined?(Liquid)
|
3
|
+
|
4
|
+
require 'support/class_options'
|
5
|
+
require 'text_filters'
|
6
|
+
|
7
|
+
require 'comatose/configuration'
|
8
|
+
require 'comatose/comatose_drop'
|
9
|
+
require 'comatose/processing_context'
|
10
|
+
require 'comatose/page_wrapper'
|
11
|
+
require 'comatose/version'
|
12
|
+
|
13
|
+
# make sure we're running inside Merb
|
14
|
+
if defined?(Merb::Plugins)
|
15
|
+
|
16
|
+
# Merb gives you a Merb::Plugins.config hash...feel free to put your stuff in your piece of it
|
17
|
+
Merb::Plugins.config[:merb_comatose] = {
|
18
|
+
:chickens => false
|
19
|
+
}
|
20
|
+
|
21
|
+
Merb::BootLoader.before_app_loads do
|
22
|
+
# require code that must be loaded before the application
|
23
|
+
end
|
24
|
+
|
25
|
+
Merb::BootLoader.after_app_loads do
|
26
|
+
# code that can be required after the application loads
|
27
|
+
end
|
28
|
+
|
29
|
+
Merb::Plugins.add_rakefiles "merb_comatose/merbtasks"
|
30
|
+
end
|