Compare commits

..

28 Commits

Author SHA1 Message Date
Tom Burns
cb807e9484 Add extension to gemspec 2013-03-19 18:30:10 -04:00
Tom Burns
e53884ce08 Remove generated C file from repo 2013-03-19 18:30:09 -04:00
Tom Burns
78fe69259d Allow floats > 9 2013-03-19 18:30:09 -04:00
Tom Burns
afe8474a2b Remove extension Makefile 2013-03-19 18:30:09 -04:00
Tobias Lutke
e404f6d8e1 fixed but with single char identifiers 2013-03-19 18:30:09 -04:00
Tobias Lutke
8867d6a65f enable weird method endings 2013-03-19 18:30:09 -04:00
Tobias Lutke
141fa898d0 enabled hard mode 2013-03-19 18:30:09 -04:00
Tobias Lutke
58b04bae74 conquered parser_tests 2013-03-19 18:30:09 -04:00
Tobias Lütke
28737171f7 wip 2013-03-19 18:30:09 -04:00
Tobias Lütke
594b6da8c5 Initial checkin 2013-03-19 18:30:09 -04:00
Tobias Lutke
cf5fa5923d remove binaries 2013-03-19 18:30:09 -04:00
Tobias Lutke
7cc10b9abf various speedups 2013-03-19 18:30:09 -04:00
Tobias Lütke
20beec2de3 Convert parser to C99 2013-03-19 18:30:09 -04:00
Tobias Lutke
06486fd21d Implement naive recusrive descent
Ragel doesn't allow us to recurse so we simply
reinvoke the parser for each step.
2013-03-19 18:30:08 -04:00
Tobias Lütke
7dcf578898 Replace regexpes with Ragel grammer
context parsing was handrolled and pretty ad-hoc
this branch exists to explore parsing the context
through a defined fsm as produced by Ragel
2013-03-19 18:30:08 -04:00
Tobias Lutke
006e7475f4 fix benchmarks 2013-03-19 18:30:08 -04:00
Dylan Smith
f7d1e1d0c1 Release 2.5.0 2013-03-06 10:51:06 -05:00
Dylan Smith
28fd2222c8 Merge branch 'remove-symbolizing' 2013-03-05 16:33:56 -05:00
Dylan Smith
9913895b81 Merge branch 'master' into remove-symbolizing
Conflicts:
	lib/liquid/variable.rb
2013-03-05 15:25:11 -05:00
Dylan Smith
d706db3bd7 Add support for filter keyword arguments. Closes #175 2013-03-05 15:17:14 -05:00
Dylan Smith
38b4543bf1 Use sets to check if methods are invokable without symbolizing. 2013-02-05 14:45:08 -05:00
Jason Roelofs
1300210f05 Convert Strainer to white-list method protection
After moving the method existence check from Context into Strainer,
updated Strainer to only accept invokation methods that were added via
filter Modules, and done in a way that respond_to? is never called,
preventing unconstrained Symbol table growth.
2013-01-16 11:14:01 -05:00
Jason Roelofs
a48e162237 Change Drop method lookup to not hit respond_to?
Class.public_method_defined? ends up diving into Ruby's core looking for
a method with the given method_or_key. This process at some point turns
method_or_key into a Symbol. This change no longer takes that path and
thus doesn't grow the Symbol table.
2013-01-16 11:07:48 -05:00
Jason Roelofs
7bcb565668 Remove #to_sym calls from Drop and Variable
Symbols are not needed here and using plain strings is nicer on Ruby
2013-01-16 09:46:17 -05:00
Jason Roelofs
c3e6cde67f Add security tests to show that the symbol table doesn't grow 2013-01-16 09:46:17 -05:00
Dylan Smith
50bd34fd78 Merge pull request #161 from Shopify/fix-filter-parser-regex
Fix filter parser regex for filter args without separating spaces.
2012-12-18 10:13:21 -08:00
Dylan Smith
ee41b3f4a3 Fix filter parser regex for filter args without separating spaces.
The regex was using \S+ to match the comma between the filters
arguments, but would continue to match idependent quote characters and
filter separators. This can result in multiple filters being interpreted as
a single one with many arguments.
2012-12-18 01:23:31 -05:00
Tobias Lütke
05d9976e16 fix benchmark 2012-10-29 16:47:57 -04:00
14 changed files with 175 additions and 834 deletions

1
.gitignore vendored
View File

@@ -7,4 +7,5 @@ pkg
*.o
*.bundle
ext/liquid/Makefile
ext/liquid/liquid_context.c

View File

@@ -1,5 +1,13 @@
# Liquid Version History
## 2.5.0 / 2013-03-06
* Prevent Object methods from being called on drops
* Avoid symbol injection from liquid
* Added break and continue statements
* Fix filter parser for args without space separators
* Add support for filter keyword arguments
## 2.4.0 / 2012-08-03
* Performance improvements

View File

@@ -1,764 +0,0 @@
/* A recursive-descent parser generated by peg 0.1.9 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define YYRULECOUNT 14
//uncomment to get more debug instrumentation
//
//#define YY_DEBUG
#include <ruby.h>
#define EMIT(sym, data) \
rb_ary_push(ctx->rb_tokens, rb_ary_new3(2, ID2SYM(rb_intern(sym)), data));
#define yy_rb_str rb_str_new(yytext, yyleng)
#define YYSTYPE VALUE
#define YY_CTX_LOCAL
#define YY_CTX_MEMBERS VALUE rb_tokens; char *p; int p_len;
#define YY_INPUT(buf, result, max_size) { \
result = ctx->p_len; \
if (result>0 || EOF == ctx->p[0]) { \
if (max_size < result) { result = max_size; } \
strncpy(buf, ctx->p, result); \
buf[result] = '\0'; \
yyprintf((stderr, "\nREFILLING %d chars now:<%s>", result, buf)); \
ctx->p += result; ctx->p_len -= result; \
yyprintf((stderr, "\nREFILLING DONE size left: %d <%s>", ctx->p_len, ctx->p)); \
} \
}
#ifndef YY_LOCAL
#define YY_LOCAL(T) static T
#endif
#ifndef YY_ACTION
#define YY_ACTION(T) static T
#endif
#ifndef YY_RULE
#define YY_RULE(T) static T
#endif
#ifndef YY_PARSE
#define YY_PARSE(T) T
#endif
#ifndef YYPARSE
#define YYPARSE yyparse
#endif
#ifndef YYPARSEFROM
#define YYPARSEFROM yyparsefrom
#endif
#ifndef YY_INPUT
#define YY_INPUT(buf, result, max_size) \
{ \
int yyc= getchar(); \
result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \
yyprintf((stderr, "<%c>", yyc)); \
}
#endif
#ifndef YY_BEGIN
#define YY_BEGIN ( ctx->begin= ctx->pos, 1)
#endif
#ifndef YY_END
#define YY_END ( ctx->end= ctx->pos, 1)
#endif
#ifdef YY_DEBUG
# define yyprintf(args) fprintf args
#else
# define yyprintf(args)
#endif
#ifndef YYSTYPE
#define YYSTYPE int
#endif
#ifndef YY_PART
typedef struct _yycontext yycontext;
typedef void (*yyaction)(yycontext *ctx, char *yytext, int yyleng);
typedef struct _yythunk { int begin, end; yyaction action; struct _yythunk *next; } yythunk;
struct _yycontext {
char *buf;
int buflen;
int pos;
int limit;
char *text;
int textlen;
int begin;
int end;
int textmax;
yythunk *thunks;
int thunkslen;
int thunkpos;
YYSTYPE yy;
YYSTYPE *val;
YYSTYPE *vals;
int valslen;
#ifdef YY_CTX_MEMBERS
YY_CTX_MEMBERS
#endif
};
#ifdef YY_CTX_LOCAL
#define YY_CTX_PARAM_ yycontext *yyctx,
#define YY_CTX_PARAM yycontext *yyctx
#define YY_CTX_ARG_ yyctx,
#define YY_CTX_ARG yyctx
#else
#define YY_CTX_PARAM_
#define YY_CTX_PARAM
#define YY_CTX_ARG_
#define YY_CTX_ARG
yycontext yyctx0;
yycontext *yyctx= &yyctx0;
#endif
YY_LOCAL(int) yyrefill(yycontext *ctx)
{
int yyn;
while (ctx->buflen - ctx->pos < 512)
{
ctx->buflen *= 2;
ctx->buf= (char *)realloc(ctx->buf, ctx->buflen);
}
YY_INPUT((ctx->buf + ctx->pos), yyn, (ctx->buflen - ctx->pos));
if (!yyn) return 0;
ctx->limit += yyn;
return 1;
}
YY_LOCAL(int) yymatchDot(yycontext *ctx)
{
if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0;
++ctx->pos;
return 1;
}
YY_LOCAL(int) yymatchChar(yycontext *ctx, int c)
{
if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0;
if ((unsigned char)ctx->buf[ctx->pos] == c)
{
++ctx->pos;
yyprintf((stderr, " ok yymatchChar(ctx, %c) @ %s\n", c, ctx->buf+ctx->pos));
return 1;
}
yyprintf((stderr, " fail yymatchChar(ctx, %c) @ %s\n", c, ctx->buf+ctx->pos));
return 0;
}
YY_LOCAL(int) yymatchString(yycontext *ctx, char *s)
{
int yysav= ctx->pos;
while (*s)
{
if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0;
if (ctx->buf[ctx->pos] != *s)
{
ctx->pos= yysav;
return 0;
}
++s;
++ctx->pos;
}
return 1;
}
YY_LOCAL(int) yymatchClass(yycontext *ctx, unsigned char *bits)
{
int c;
if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0;
c= (unsigned char)ctx->buf[ctx->pos];
if (bits[c >> 3] & (1 << (c & 7)))
{
++ctx->pos;
yyprintf((stderr, " ok yymatchClass @ %s\n", ctx->buf+ctx->pos));
return 1;
}
yyprintf((stderr, " fail yymatchClass @ %s\n", ctx->buf+ctx->pos));
return 0;
}
YY_LOCAL(void) yyDo(yycontext *ctx, yyaction action, int begin, int end)
{
while (ctx->thunkpos >= ctx->thunkslen)
{
ctx->thunkslen *= 2;
ctx->thunks= (yythunk *)realloc(ctx->thunks, sizeof(yythunk) * ctx->thunkslen);
}
ctx->thunks[ctx->thunkpos].begin= begin;
ctx->thunks[ctx->thunkpos].end= end;
ctx->thunks[ctx->thunkpos].action= action;
++ctx->thunkpos;
}
YY_LOCAL(int) yyText(yycontext *ctx, int begin, int end)
{
int yyleng= end - begin;
if (yyleng <= 0)
yyleng= 0;
else
{
while (ctx->textlen < (yyleng + 1))
{
ctx->textlen *= 2;
ctx->text= (char *)realloc(ctx->text, ctx->textlen);
}
memcpy(ctx->text, ctx->buf + begin, yyleng);
}
ctx->text[yyleng]= '\0';
return yyleng;
}
YY_LOCAL(void) yyDone(yycontext *ctx)
{
int pos;
for (pos= 0; pos < ctx->thunkpos; ++pos)
{
yythunk *thunk= &ctx->thunks[pos];
int yyleng= thunk->end ? yyText(ctx, thunk->begin, thunk->end) : thunk->begin;
yyprintf((stderr, "DO [%d] %p %s\n", pos, thunk->action, ctx->text));
thunk->action(ctx, ctx->text, yyleng);
}
ctx->thunkpos= 0;
}
YY_LOCAL(void) yyCommit(yycontext *ctx)
{
if ((ctx->limit -= ctx->pos))
{
memmove(ctx->buf, ctx->buf + ctx->pos, ctx->limit);
}
ctx->begin -= ctx->pos;
ctx->end -= ctx->pos;
ctx->pos= ctx->thunkpos= 0;
}
YY_LOCAL(int) yyAccept(yycontext *ctx, int tp0)
{
if (tp0)
{
fprintf(stderr, "accept denied at %d\n", tp0);
return 0;
}
else
{
yyDone(ctx);
yyCommit(ctx);
}
return 1;
}
YY_LOCAL(void) yyPush(yycontext *ctx, char *text, int count) { ctx->val += count; }
YY_LOCAL(void) yyPop(yycontext *ctx, char *text, int count) { ctx->val -= count; }
YY_LOCAL(void) yySet(yycontext *ctx, char *text, int count) { ctx->val[count]= ctx->yy; }
#endif /* YY_PART */
#define YYACCEPT yyAccept(ctx, yythunkpos0)
YY_RULE(int) yy_digit(yycontext *ctx); /* 14 */
YY_RULE(int) yy_float(yycontext *ctx); /* 13 */
YY_RULE(int) yy_integer(yycontext *ctx); /* 12 */
YY_RULE(int) yy_rangelet(yycontext *ctx); /* 11 */
YY_RULE(int) yy_identifier(yycontext *ctx); /* 10 */
YY_RULE(int) yy_accessors(yycontext *ctx); /* 9 */
YY_RULE(int) yy_numeric(yycontext *ctx); /* 8 */
YY_RULE(int) yy_string(yycontext *ctx); /* 7 */
YY_RULE(int) yy_const(yycontext *ctx); /* 6 */
YY_RULE(int) yy_hash(yycontext *ctx); /* 5 */
YY_RULE(int) yy_range(yycontext *ctx); /* 4 */
YY_RULE(int) yy_entity(yycontext *ctx); /* 3 */
YY_RULE(int) yy_primary(yycontext *ctx); /* 2 */
YY_RULE(int) yy_grammar(yycontext *ctx); /* 1 */
YY_ACTION(void) yy_4_const(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_4_const\n"));
yy = Qnil; ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_3_const(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_3_const\n"));
yy = Qnil; ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_2_const(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_2_const\n"));
yy = Qfalse; ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_1_const(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_const\n"));
yy = Qtrue; ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_1_integer(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_integer\n"));
yy = rb_funcall(rb_cObject, rb_intern("Integer"), 1, yy_rb_str); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_1_float(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_float\n"));
yy = rb_funcall(rb_cObject, rb_intern("Float"), 1, yy_rb_str); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_2_string(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_2_string\n"));
yy = yy_rb_str; ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_1_string(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_string\n"));
yy = yy_rb_str; ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_1_range(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_range\n"));
EMIT("range", Qnil); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_1_rangelet(yycontext *ctx, char *yytext, int yyleng)
{
#define var ctx->val[-1]
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_rangelet\n"));
EMIT("id", var); ;
#undef yythunkpos
#undef yypos
#undef yy
#undef var
}
YY_ACTION(void) yy_1_entity(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_entity\n"));
EMIT("id", yy_rb_str); EMIT("lookup", Qnil); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_5_accessors(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_5_accessors\n"));
EMIT("id", yy_rb_str); EMIT("call", Qnil); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_4_accessors(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_4_accessors\n"));
EMIT("buildin", rb_str_new2("size")); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_3_accessors(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_3_accessors\n"));
EMIT("buildin", rb_str_new2("last")); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_2_accessors(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_2_accessors\n"));
EMIT("buildin", rb_str_new2("first")); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_1_accessors(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_accessors\n"));
EMIT("call", Qnil); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_ACTION(void) yy_3_primary(yycontext *ctx, char *yytext, int yyleng)
{
#define var ctx->val[-1]
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_3_primary\n"));
EMIT("id", var); ;
#undef yythunkpos
#undef yypos
#undef yy
#undef var
}
YY_ACTION(void) yy_2_primary(yycontext *ctx, char *yytext, int yyleng)
{
#define var ctx->val[-1]
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_2_primary\n"));
EMIT("id", var); ;
#undef yythunkpos
#undef yypos
#undef yy
#undef var
}
YY_ACTION(void) yy_1_primary(yycontext *ctx, char *yytext, int yyleng)
{
#define var ctx->val[-1]
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_primary\n"));
EMIT("id", var); ;
#undef yythunkpos
#undef yypos
#undef yy
#undef var
}
YY_ACTION(void) yy_1_grammar(yycontext *ctx, char *yytext, int yyleng)
{
#define yy ctx->yy
#define yypos ctx->pos
#define yythunkpos ctx->thunkpos
yyprintf((stderr, "do yy_1_grammar\n"));
EMIT("lookup", Qnil); ;
#undef yythunkpos
#undef yypos
#undef yy
}
YY_RULE(int) yy_digit(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "digit")); if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\000\377\003\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l1;
yyprintf((stderr, " ok %s @ %s\n", "digit", ctx->buf+ctx->pos));
return 1;
l1:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "digit", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_float(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "float")); yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l2;
{ int yypos3= ctx->pos, yythunkpos3= ctx->thunkpos; if (!yymatchChar(ctx, '-')) goto l3; goto l4;
l3:; ctx->pos= yypos3; ctx->thunkpos= yythunkpos3;
}
l4:; if (!yy_digit(ctx)) goto l2; if (!yymatchChar(ctx, '.')) goto l2; if (!yy_digit(ctx)) goto l2;
l5:;
{ int yypos6= ctx->pos, yythunkpos6= ctx->thunkpos; if (!yy_digit(ctx)) goto l6; goto l5;
l6:; ctx->pos= yypos6; ctx->thunkpos= yythunkpos6;
} yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l2; yyDo(ctx, yy_1_float, ctx->begin, ctx->end);
yyprintf((stderr, " ok %s @ %s\n", "float", ctx->buf+ctx->pos));
return 1;
l2:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "float", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_integer(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "integer")); yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l7;
{ int yypos8= ctx->pos, yythunkpos8= ctx->thunkpos; if (!yymatchChar(ctx, '-')) goto l8; goto l9;
l8:; ctx->pos= yypos8; ctx->thunkpos= yythunkpos8;
}
l9:; if (!yy_digit(ctx)) goto l7;
l10:;
{ int yypos11= ctx->pos, yythunkpos11= ctx->thunkpos; if (!yy_digit(ctx)) goto l11; goto l10;
l11:; ctx->pos= yypos11; ctx->thunkpos= yythunkpos11;
} yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l7; yyDo(ctx, yy_1_integer, ctx->begin, ctx->end);
yyprintf((stderr, " ok %s @ %s\n", "integer", ctx->buf+ctx->pos));
return 1;
l7:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "integer", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_rangelet(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; yyDo(ctx, yyPush, 1, 0);
yyprintf((stderr, "%s\n", "rangelet"));
{ int yypos13= ctx->pos, yythunkpos13= ctx->thunkpos; if (!yy_integer(ctx)) goto l14; yyDo(ctx, yySet, -1, 0); yyDo(ctx, yy_1_rangelet, ctx->begin, ctx->end); goto l13;
l14:; ctx->pos= yypos13; ctx->thunkpos= yythunkpos13; if (!yy_entity(ctx)) goto l12;
}
l13:;
yyprintf((stderr, " ok %s @ %s\n", "rangelet", ctx->buf+ctx->pos)); yyDo(ctx, yyPop, 1, 0);
return 1;
l12:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "rangelet", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_identifier(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "identifier")); if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\000\000\000\376\377\377\007\376\377\377\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l15;
l16:;
{ int yypos17= ctx->pos, yythunkpos17= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\040\377\003\376\377\377\207\376\377\377\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l17; goto l16;
l17:; ctx->pos= yypos17; ctx->thunkpos= yythunkpos17;
}
{ int yypos18= ctx->pos, yythunkpos18= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\002\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l18; goto l19;
l18:; ctx->pos= yypos18; ctx->thunkpos= yythunkpos18;
}
l19:;
yyprintf((stderr, " ok %s @ %s\n", "identifier", ctx->buf+ctx->pos));
return 1;
l15:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "identifier", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_accessors(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "accessors"));
{ int yypos21= ctx->pos, yythunkpos21= ctx->thunkpos; if (!yy_hash(ctx)) goto l22; yyDo(ctx, yy_1_accessors, ctx->begin, ctx->end); goto l21;
l22:; ctx->pos= yypos21; ctx->thunkpos= yythunkpos21; if (!yymatchString(ctx, ".first")) goto l23; yyDo(ctx, yy_2_accessors, ctx->begin, ctx->end); goto l21;
l23:; ctx->pos= yypos21; ctx->thunkpos= yythunkpos21; if (!yymatchString(ctx, ".last")) goto l24; yyDo(ctx, yy_3_accessors, ctx->begin, ctx->end); goto l21;
l24:; ctx->pos= yypos21; ctx->thunkpos= yythunkpos21; if (!yymatchString(ctx, ".size")) goto l25; yyDo(ctx, yy_4_accessors, ctx->begin, ctx->end); goto l21;
l25:; ctx->pos= yypos21; ctx->thunkpos= yythunkpos21; if (!yymatchChar(ctx, '.')) goto l20; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l20; if (!yy_identifier(ctx)) goto l20; yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l20; yyDo(ctx, yy_5_accessors, ctx->begin, ctx->end);
}
l21:;
yyprintf((stderr, " ok %s @ %s\n", "accessors", ctx->buf+ctx->pos));
return 1;
l20:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "accessors", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_numeric(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "numeric"));
{ int yypos27= ctx->pos, yythunkpos27= ctx->thunkpos; if (!yy_float(ctx)) goto l28; goto l27;
l28:; ctx->pos= yypos27; ctx->thunkpos= yythunkpos27; if (!yy_integer(ctx)) goto l26;
}
l27:;
yyprintf((stderr, " ok %s @ %s\n", "numeric", ctx->buf+ctx->pos));
return 1;
l26:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "numeric", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_string(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "string"));
{ int yypos30= ctx->pos, yythunkpos30= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l31; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l31;
l32:;
{ int yypos33= ctx->pos, yythunkpos33= ctx->thunkpos;
{ int yypos34= ctx->pos, yythunkpos34= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l34; goto l33;
l34:; ctx->pos= yypos34; ctx->thunkpos= yythunkpos34;
} if (!yymatchDot(ctx)) goto l33; goto l32;
l33:; ctx->pos= yypos33; ctx->thunkpos= yythunkpos33;
} yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l31; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l31; yyDo(ctx, yy_1_string, ctx->begin, ctx->end); goto l30;
l31:; ctx->pos= yypos30; ctx->thunkpos= yythunkpos30; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l29; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l29;
l35:;
{ int yypos36= ctx->pos, yythunkpos36= ctx->thunkpos;
{ int yypos37= ctx->pos, yythunkpos37= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l37; goto l36;
l37:; ctx->pos= yypos37; ctx->thunkpos= yythunkpos37;
} if (!yymatchDot(ctx)) goto l36; goto l35;
l36:; ctx->pos= yypos36; ctx->thunkpos= yythunkpos36;
} yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l29; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l29; yyDo(ctx, yy_2_string, ctx->begin, ctx->end);
}
l30:;
yyprintf((stderr, " ok %s @ %s\n", "string", ctx->buf+ctx->pos));
return 1;
l29:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "string", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_const(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "const"));
{ int yypos39= ctx->pos, yythunkpos39= ctx->thunkpos; if (!yymatchString(ctx, "true")) goto l40; yyDo(ctx, yy_1_const, ctx->begin, ctx->end); goto l39;
l40:; ctx->pos= yypos39; ctx->thunkpos= yythunkpos39; if (!yymatchString(ctx, "false")) goto l41; yyDo(ctx, yy_2_const, ctx->begin, ctx->end); goto l39;
l41:; ctx->pos= yypos39; ctx->thunkpos= yythunkpos39; if (!yymatchString(ctx, "nil")) goto l42; yyDo(ctx, yy_3_const, ctx->begin, ctx->end); goto l39;
l42:; ctx->pos= yypos39; ctx->thunkpos= yythunkpos39; if (!yymatchString(ctx, "null")) goto l38; yyDo(ctx, yy_4_const, ctx->begin, ctx->end);
}
l39:;
yyprintf((stderr, " ok %s @ %s\n", "const", ctx->buf+ctx->pos));
return 1;
l38:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "const", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_hash(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "hash")); if (!yymatchChar(ctx, '[')) goto l43;
{ int yypos44= ctx->pos, yythunkpos44= ctx->thunkpos; if (!yy_primary(ctx)) goto l45; goto l44;
l45:; ctx->pos= yypos44; ctx->thunkpos= yythunkpos44; if (!yy_entity(ctx)) goto l43;
}
l44:; if (!yymatchChar(ctx, ']')) goto l43;
yyprintf((stderr, " ok %s @ %s\n", "hash", ctx->buf+ctx->pos));
return 1;
l43:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "hash", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_range(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "range")); if (!yymatchChar(ctx, '(')) goto l46; if (!yy_rangelet(ctx)) goto l46; if (!yymatchString(ctx, "..")) goto l46; if (!yy_rangelet(ctx)) goto l46; if (!yymatchChar(ctx, ')')) goto l46; yyDo(ctx, yy_1_range, ctx->begin, ctx->end);
yyprintf((stderr, " ok %s @ %s\n", "range", ctx->buf+ctx->pos));
return 1;
l46:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "range", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_entity(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "entity")); yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l47; if (!yy_identifier(ctx)) goto l47; yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l47; yyDo(ctx, yy_1_entity, ctx->begin, ctx->end);
l48:;
{ int yypos49= ctx->pos, yythunkpos49= ctx->thunkpos; if (!yy_accessors(ctx)) goto l49; goto l48;
l49:; ctx->pos= yypos49; ctx->thunkpos= yythunkpos49;
}
yyprintf((stderr, " ok %s @ %s\n", "entity", ctx->buf+ctx->pos));
return 1;
l47:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "entity", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_primary(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; yyDo(ctx, yyPush, 1, 0);
yyprintf((stderr, "%s\n", "primary"));
{ int yypos51= ctx->pos, yythunkpos51= ctx->thunkpos; if (!yy_const(ctx)) goto l52; yyDo(ctx, yySet, -1, 0); yyDo(ctx, yy_1_primary, ctx->begin, ctx->end); goto l51;
l52:; ctx->pos= yypos51; ctx->thunkpos= yythunkpos51; if (!yy_string(ctx)) goto l53; yyDo(ctx, yySet, -1, 0); yyDo(ctx, yy_2_primary, ctx->begin, ctx->end); goto l51;
l53:; ctx->pos= yypos51; ctx->thunkpos= yythunkpos51; if (!yy_numeric(ctx)) goto l50; yyDo(ctx, yySet, -1, 0); yyDo(ctx, yy_3_primary, ctx->begin, ctx->end);
}
l51:;
yyprintf((stderr, " ok %s @ %s\n", "primary", ctx->buf+ctx->pos)); yyDo(ctx, yyPop, 1, 0);
return 1;
l50:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "primary", ctx->buf+ctx->pos));
return 0;
}
YY_RULE(int) yy_grammar(yycontext *ctx)
{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos;
yyprintf((stderr, "%s\n", "grammar"));
{ int yypos55= ctx->pos, yythunkpos55= ctx->thunkpos; if (!yy_primary(ctx)) goto l56; goto l55;
l56:; ctx->pos= yypos55; ctx->thunkpos= yythunkpos55; if (!yy_entity(ctx)) goto l57; goto l55;
l57:; ctx->pos= yypos55; ctx->thunkpos= yythunkpos55; if (!yy_range(ctx)) goto l58; goto l55;
l58:; ctx->pos= yypos55; ctx->thunkpos= yythunkpos55; if (!yy_hash(ctx)) goto l54; yyDo(ctx, yy_1_grammar, ctx->begin, ctx->end);
}
l55:;
yyprintf((stderr, " ok %s @ %s\n", "grammar", ctx->buf+ctx->pos));
return 1;
l54:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0;
yyprintf((stderr, " fail %s @ %s\n", "grammar", ctx->buf+ctx->pos));
return 0;
}
#ifndef YY_PART
typedef int (*yyrule)(yycontext *ctx);
YY_PARSE(int) YYPARSEFROM(YY_CTX_PARAM_ yyrule yystart)
{
int yyok;
if (!yyctx->buflen)
{
yyctx->buflen= 1024;
yyctx->buf= (char *)malloc(yyctx->buflen);
yyctx->textlen= 1024;
yyctx->text= (char *)malloc(yyctx->textlen);
yyctx->thunkslen= 32;
yyctx->thunks= (yythunk *)malloc(sizeof(yythunk) * yyctx->thunkslen);
yyctx->valslen= 32;
yyctx->vals= (YYSTYPE *)malloc(sizeof(YYSTYPE) * yyctx->valslen);
yyctx->begin= yyctx->end= yyctx->pos= yyctx->limit= yyctx->thunkpos= 0;
}
yyctx->begin= yyctx->end= yyctx->pos;
yyctx->thunkpos= 0;
yyctx->val= yyctx->vals;
yyok= yystart(yyctx);
if (yyok) yyDone(yyctx);
yyCommit(yyctx);
return yyok;
}
YY_PARSE(int) YYPARSE(YY_CTX_PARAM)
{
return YYPARSEFROM(YY_CTX_ARG_ yy_grammar);
}
#endif
VALUE liquid_context_parse_impl(VALUE self, VALUE text) {
char *p;
int len;
yycontext ctx;
memset(&ctx, 0, sizeof(yycontext));
ctx.p = RSTRING_PTR(text);
ctx.p_len = (int) RSTRING_LEN(text);
ctx.rb_tokens = rb_ary_new();
yyparse(&ctx);
return ctx.rb_tokens;
}

View File

@@ -40,6 +40,7 @@ module Liquid
filters.each do |f|
raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module)
Strainer.add_known_filter(f)
strainer.extend(f)
end
end
@@ -72,11 +73,7 @@ module Liquid
end
def invoke(method, *args)
if strainer.respond_to?(method)
strainer.__send__(method, *args)
else
args.first
end
strainer.invoke(method, *args)
end
# Push new local scope on the stack. use <tt>Context#stack</tt> instead

View File

@@ -1,3 +1,5 @@
require 'set'
module Liquid
# A drop in liquid is a class which allows you to export DOM like things to liquid.
@@ -31,8 +33,8 @@ module Liquid
# called by liquid to invoke a drop
def invoke_drop(method_or_key)
if method_or_key && method_or_key != EMPTY_STRING && self.class.public_method_defined?(method_or_key.to_s.to_sym)
send(method_or_key.to_s.to_sym)
if method_or_key && method_or_key != EMPTY_STRING && self.class.invokable?(method_or_key)
send(method_or_key)
else
before_method(method_or_key)
end
@@ -47,5 +49,13 @@ module Liquid
end
alias :[] :invoke_drop
private
# Check for method existence without invoking respond_to?, which creates symbols
def self.invokable?(method_name)
@invokable_methods ||= Set.new((public_instance_methods - Liquid::Drop.public_instance_methods).map(&:to_s))
@invokable_methods.include?(method_name.to_s)
end
end
end

View File

@@ -2,24 +2,15 @@ require 'set'
module Liquid
parent_object = if defined? BlankObject
BlankObject
else
Object
end
# Strainer is the parent class for the filters system.
# New filters are mixed into the strainer class which is then instanciated for each liquid template render run.
# New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
#
# One of the strainer's responsibilities is to keep malicious method calls out
class Strainer < parent_object #:nodoc:
INTERNAL_METHOD = /^__/
@@required_methods = Set.new([:__id__, :__send__, :respond_to?, :kind_of?, :extend, :methods, :singleton_methods, :class, :object_id])
# Ruby 1.9.2 introduces Object#respond_to_missing?, which is invoked by Object#respond_to?
@@required_methods << :respond_to_missing? if Object.respond_to? :respond_to_missing?
# The Strainer only allows method calls defined in filters given to it via Strainer.global_filter,
# Context#add_filters or Template.register_filter
class Strainer #:nodoc:
@@filters = {}
@@known_filters = Set.new
@@known_methods = Set.new
def initialize(context)
@context = context
@@ -27,28 +18,36 @@ module Liquid
def self.global_filter(filter)
raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module)
add_known_filter(filter)
@@filters[filter.name] = filter
end
def self.add_known_filter(filter)
unless @@known_filters.include?(filter)
@@method_blacklist ||= Set.new(Strainer.instance_methods.map(&:to_s))
new_methods = filter.instance_methods.map(&:to_s)
new_methods.reject!{ |m| @@method_blacklist.include?(m) }
@@known_methods.merge(new_methods)
@@known_filters.add(filter)
end
end
def self.create(context)
strainer = Strainer.new(context)
@@filters.each { |k,m| strainer.extend(m) }
strainer
end
def respond_to?(method, include_private = false)
method_name = method.to_s
return false if method_name =~ INTERNAL_METHOD
return false if @@required_methods.include?(method_name)
super
def invoke(method, *args)
if invokable?(method)
send(method, *args)
else
args.first
end
end
# remove all standard methods from the bucket so circumvent security
# problems
instance_methods.each do |m|
unless @@required_methods.include?(m.to_sym)
undef_method m
end
def invokable?(method)
@@known_methods.include?(method.to_s) && respond_to?(method)
end
end
end

View File

@@ -11,7 +11,7 @@ module Liquid
# {{ user | link }}
#
class Variable
FilterParser = /(?:#{FilterSeparator}|(?:\s*(?!(?:#{FilterSeparator}))(?:#{QuotedFragment}|\S+)\s*)+)/o
FilterParser = /(?:#{FilterSeparator}|(?:\s*(?:#{QuotedFragment}|#{ArgumentSeparator})\s*)+)/o
attr_accessor :filters, :name
def initialize(markup)
@@ -23,10 +23,10 @@ module Liquid
if match[2].match(/#{FilterSeparator}\s*(.*)/o)
filters = Regexp.last_match(1).scan(FilterParser)
filters.each do |f|
if matches = f.match(/\s*(\w+)/)
if matches = f.match(/\s*(\w+)(?:\s*#{FilterArgumentSeparator}(.*))?/)
filtername = matches[1]
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/o).flatten
@filters << [filtername.to_sym, filterargs]
filterargs = matches[2].to_s.scan(/(?:\A|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten
@filters << [filtername, filterargs]
end
end
end
@@ -36,9 +36,16 @@ module Liquid
def render(context)
return '' if @name.nil?
@filters.inject(context[@name]) do |output, filter|
filterargs = filter[1].to_a.collect do |a|
context[a]
filterargs = []
keyword_args = {}
filter[1].to_a.each do |a|
if matches = a.match(/\A#{TagAttributes}\z/o)
keyword_args[matches[1]] = context[matches[2]]
else
filterargs << context[a]
end
end
filterargs << keyword_args unless keyword_args.empty?
begin
output = context.invoke(filter[0], output, *filterargs)
rescue FilterNotFound

View File

@@ -2,7 +2,7 @@
Gem::Specification.new do |s|
s.name = "liquid"
s.version = "2.4.1"
s.version = "2.5.0"
s.platform = Gem::Platform::RUBY
s.summary = "A secure, non-evaling end user template engine with aesthetic markup."
s.authors = ["Tobias Luetke"]
@@ -13,9 +13,12 @@ Gem::Specification.new do |s|
s.required_rubygems_version = ">= 1.3.7"
s.test_files = Dir.glob("{test}/**/*")
s.files = Dir.glob("{lib}/**/*") + %w(MIT-LICENSE README.md)
s.files = Dir.glob("{lib}/**/*") +
Dir.glob("{ext}/**/*") + %w(MIT-LICENSE README.md)
s.extensions = ['ext/liquid/extconf.rb']
s.extra_rdoc_files = ["History.md", "README.md"]
s.require_path = "lib"
s.require_paths = ["lib", "ext"]
end

View File

@@ -189,10 +189,10 @@ class ContextTest < Test::Unit::TestCase
end
context = Context.new
methods_before = context.strainer.methods.map { |method| method.to_s }
assert_equal "Wookie", context.invoke("hi", "Wookie")
context.add_filters(filter)
methods_after = context.strainer.methods.map { |method| method.to_s }
assert_equal (methods_before + ["hi"]).sort, methods_after.sort
assert_equal "Wookie hi!", context.invoke("hi", "Wookie")
end
def test_add_item_in_outer_scope

View File

@@ -115,6 +115,13 @@ class DropsTest < Test::Unit::TestCase
assert_equal ' ', output
end
def test_object_methods_not_allowed
[:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method|
output = Liquid::Template.parse(" {{ product.#{method} }} ").render('product' => ProductDrop.new)
assert_equal ' ', output
end
end
def test_scope
assert_equal '1', Liquid::Template.parse( '{{ context.scopes }}' ).render('context' => ContextDrop.new)
assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ context.scopes }}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])

View File

@@ -16,6 +16,12 @@ module CanadianMoneyFilter
end
end
module SubstituteFilter
def substitute(input, params={})
input.gsub(/%\{(\w+)\}/) { |match| params[$1] }
end
end
class FiltersTest < Test::Unit::TestCase
include Liquid
@@ -92,6 +98,13 @@ class FiltersTest < Test::Unit::TestCase
assert_equal 1000, Variable.new("var | xyzzy").render(@context)
end
def test_filter_with_keyword_arguments
@context['surname'] = 'john'
@context.add_filters(SubstituteFilter)
output = Variable.new(%! 'hello %{first_name}, %{last_name}' | substitute: first_name: surname, last_name: 'doe' !).render(@context)
assert_equal 'hello john, doe', output
end
end
class FiltersInTemplate < Test::Unit::TestCase

View File

@@ -38,4 +38,27 @@ class SecurityTest < Test::Unit::TestCase
assert_equal expected, Template.parse(text).render(@assigns, :filters => SecurityFilter)
end
def test_does_not_add_filters_to_symbol_table
current_symbols = Symbol.all_symbols
test = %( {{ "some_string" | a_bad_filter }} )
template = Template.parse(test)
assert_equal [], (Symbol.all_symbols - current_symbols)
template.render
assert_equal [], (Symbol.all_symbols - current_symbols)
end
def test_does_not_add_drop_methods_to_symbol_table
current_symbols = Symbol.all_symbols
drop = Drop.new
drop.invoke_drop("custom_method_1")
drop.invoke_drop("custom_method_2")
drop.invoke_drop("custom_method_3")
assert_equal [], (Symbol.all_symbols - current_symbols)
end
end # SecurityTest

View File

@@ -3,23 +3,50 @@ require 'test_helper'
class StrainerTest < Test::Unit::TestCase
include Liquid
module AccessScopeFilters
def public_filter
"public"
end
def private_filter
"private"
end
private :private_filter
end
Strainer.global_filter(AccessScopeFilters)
def test_strainer
strainer = Strainer.create(nil)
assert_equal false, strainer.respond_to?('__test__')
assert_equal false, strainer.respond_to?('test')
assert_equal false, strainer.respond_to?('instance_eval')
assert_equal false, strainer.respond_to?('__send__')
assert_equal true, strainer.respond_to?('size') # from the standard lib
assert_equal 5, strainer.invoke('size', 'input')
assert_equal "public", strainer.invoke("public_filter")
end
def test_should_respond_to_two_parameters
def test_strainer_only_invokes_public_filter_methods
strainer = Strainer.create(nil)
assert_equal true, strainer.respond_to?('size', false)
assert_equal false, strainer.invokable?('__test__')
assert_equal false, strainer.invokable?('test')
assert_equal false, strainer.invokable?('instance_eval')
assert_equal false, strainer.invokable?('__send__')
assert_equal true, strainer.invokable?('size') # from the standard lib
end
# Asserts that Object#respond_to_missing? is not being undefined in Ruby versions where it has been implemented
# Currently this method is only present in Ruby v1.9.2, or higher
def test_object_respond_to_missing
assert_equal Object.respond_to?(:respond_to_missing?), Strainer.create(nil).respond_to?(:respond_to_missing?)
def test_strainer_returns_nil_if_no_filter_method_found
strainer = Strainer.create(nil)
assert_nil strainer.invoke("private_filter")
assert_nil strainer.invoke("undef_the_filter")
end
def test_strainer_returns_first_argument_if_no_method_and_arguments_given
strainer = Strainer.create(nil)
assert_equal "password", strainer.invoke("undef_the_method", "password")
end
def test_strainer_only_allows_methods_defined_in_filters
strainer = Strainer.create(nil)
assert_equal "1 + 1", strainer.invoke("instance_eval", "1 + 1")
assert_equal "puts", strainer.invoke("__send__", "puts", "Hi Mom")
assert_equal "has_method?", strainer.invoke("invoke", "has_method?", "invoke")
end
end # StrainerTest

View File

@@ -11,67 +11,71 @@ class VariableTest < Test::Unit::TestCase
def test_filters
var = Variable.new('hello | textileze')
assert_equal 'hello', var.name
assert_equal [[:textileze,[]]], var.filters
assert_equal [["textileze",[]]], var.filters
var = Variable.new('hello | textileze | paragraph')
assert_equal 'hello', var.name
assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
assert_equal [["textileze",[]], ["paragraph",[]]], var.filters
var = Variable.new(%! hello | strftime: '%Y'!)
assert_equal 'hello', var.name
assert_equal [[:strftime,["'%Y'"]]], var.filters
assert_equal [["strftime",["'%Y'"]]], var.filters
var = Variable.new(%! 'typo' | link_to: 'Typo', true !)
assert_equal %!'typo'!, var.name
assert_equal [[:link_to,["'Typo'", "true"]]], var.filters
assert_equal [["link_to",["'Typo'", "true"]]], var.filters
var = Variable.new(%! 'typo' | link_to: 'Typo', false !)
assert_equal %!'typo'!, var.name
assert_equal [[:link_to,["'Typo'", "false"]]], var.filters
assert_equal [["link_to",["'Typo'", "false"]]], var.filters
var = Variable.new(%! 'foo' | repeat: 3 !)
assert_equal %!'foo'!, var.name
assert_equal [[:repeat,["3"]]], var.filters
assert_equal [["repeat",["3"]]], var.filters
var = Variable.new(%! 'foo' | repeat: 3, 3 !)
assert_equal %!'foo'!, var.name
assert_equal [[:repeat,["3","3"]]], var.filters
assert_equal [["repeat",["3","3"]]], var.filters
var = Variable.new(%! 'foo' | repeat: 3, 3, 3 !)
assert_equal %!'foo'!, var.name
assert_equal [[:repeat,["3","3","3"]]], var.filters
assert_equal [["repeat",["3","3","3"]]], var.filters
var = Variable.new(%! hello | strftime: '%Y, okay?'!)
assert_equal 'hello', var.name
assert_equal [[:strftime,["'%Y, okay?'"]]], var.filters
assert_equal [["strftime",["'%Y, okay?'"]]], var.filters
var = Variable.new(%! hello | things: "%Y, okay?", 'the other one'!)
assert_equal 'hello', var.name
assert_equal [[:things,["\"%Y, okay?\"","'the other one'"]]], var.filters
assert_equal [["things",["\"%Y, okay?\"","'the other one'"]]], var.filters
end
def test_filter_with_date_parameter
var = Variable.new(%! '2006-06-06' | date: "%m/%d/%Y"!)
assert_equal "'2006-06-06'", var.name
assert_equal [[:date,["\"%m/%d/%Y\""]]], var.filters
assert_equal [["date",["\"%m/%d/%Y\""]]], var.filters
end
def test_filters_without_whitespace
var = Variable.new('hello | textileze | paragraph')
assert_equal 'hello', var.name
assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
assert_equal [["textileze",[]], ["paragraph",[]]], var.filters
var = Variable.new('hello|textileze|paragraph')
assert_equal 'hello', var.name
assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
assert_equal [["textileze",[]], ["paragraph",[]]], var.filters
var = Variable.new("hello|replace:'foo','bar'|textileze")
assert_equal 'hello', var.name
assert_equal [["replace", ["'foo'", "'bar'"]], ["textileze", []]], var.filters
end
def test_symbol
var = Variable.new("http://disney.com/logo.gif | image: 'med' ")
assert_equal 'http://disney.com/logo.gif', var.name
assert_equal [[:image,["'med'"]]], var.filters
assert_equal [["image",["'med'"]]], var.filters
end
def test_string_single_quoted
@@ -103,6 +107,12 @@ class VariableTest < Test::Unit::TestCase
var = Variable.new(%| test.test |)
assert_equal 'test.test', var.name
end
def test_filter_with_keyword_arguments
var = Variable.new(%! hello | things: greeting: "world", farewell: 'goodbye'!)
assert_equal 'hello', var.name
assert_equal [['things',["greeting: \"world\"","farewell: 'goodbye'"]]], var.filters
end
end