liquid_assets 0.1.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +5 -12
- data/Gemfile.lock +40 -18
- data/LICENSE.txt +2 -0
- data/README.md +15 -146
- data/Rakefile +2 -16
- data/lib/liquid_assets/config.rb +36 -9
- data/lib/liquid_assets/engine.rb +4 -4
- data/lib/liquid_assets/eval.rb +20 -0
- data/lib/liquid_assets/{tilt_engine.rb → pipeline_template_engine.rb} +8 -12
- data/lib/liquid_assets/resolver.rb +68 -0
- data/lib/liquid_assets/template_handler.rb +6 -20
- data/lib/liquid_assets/tiny_liquid.rb +11 -7
- data/lib/liquid_assets/version.rb +1 -1
- data/lib/liquid_assets.rb +5 -2
- data/liquid_assets.gemspec +23 -71
- data/test/helper.rb +1 -1
- data/test/test_liquid_assets.rb +49 -6
- data/vendor/assets/javascripts/liquid_assets.js.erb +228 -0
- data/vendor/tinyliquid.js +9 -6
- metadata +53 -85
- data/.document +0 -5
- data/VERSION +0 -1
- data/vendor/assets/javascripts/liquid_assets.js +0 -57
data/lib/liquid_assets.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
require 'liquid_assets/version'
|
2
|
+
require 'liquid_assets/eval'
|
2
3
|
require 'liquid_assets/config'
|
3
4
|
require 'liquid_assets/template_handler'
|
4
|
-
|
5
|
+
|
6
|
+
require 'liquid_assets/resolver'
|
7
|
+
|
5
8
|
module LiquidAssets
|
6
9
|
|
7
10
|
extend Config
|
8
11
|
|
9
12
|
autoload :TinyLiquid, 'liquid_assets/tiny_liquid'
|
10
|
-
autoload :
|
13
|
+
autoload :PipelineTemplateEngine, 'liquid_assets/pipeline_template_engine'
|
11
14
|
|
12
15
|
if defined? Rails
|
13
16
|
require 'liquid_assets/engine'
|
data/liquid_assets.gemspec
CHANGED
@@ -1,76 +1,28 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'liquid_assets/version'
|
5
5
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "liquid_assets"
|
8
|
+
spec.version = LiquidAssets::VERSION
|
9
|
+
spec.authors = ["Nathan Stitt"]
|
10
|
+
spec.email = ["nathan@stitt.org"]
|
11
|
+
spec.description = %q{A rails engine that supports writing both server and client side templates in Liqud markup}
|
12
|
+
spec.summary = %q{Liquid formmated views and assets}
|
13
|
+
spec.homepage = "http://github.com/nathanstitt/liquid_assets"
|
14
|
+
spec.license = "MIT"
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
s.email = "nathan@stitt.org"
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE.txt",
|
17
|
-
"README.md"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
"Gemfile",
|
22
|
-
"Gemfile.lock",
|
23
|
-
"LICENSE.txt",
|
24
|
-
"README.md",
|
25
|
-
"Rakefile",
|
26
|
-
"VERSION",
|
27
|
-
"lib/liquid_assets.rb",
|
28
|
-
"lib/liquid_assets/config.rb",
|
29
|
-
"lib/liquid_assets/engine.rb",
|
30
|
-
"lib/liquid_assets/template_handler.rb",
|
31
|
-
"lib/liquid_assets/tilt_engine.rb",
|
32
|
-
"lib/liquid_assets/tiny_liquid.rb",
|
33
|
-
"lib/liquid_assets/version.rb",
|
34
|
-
"liquid_assets.gemspec",
|
35
|
-
"test/helper.rb",
|
36
|
-
"test/test_liquid_assets.rb",
|
37
|
-
"vendor/assets/javascripts/liquid_assets.js",
|
38
|
-
"vendor/tinyliquid.js"
|
39
|
-
]
|
40
|
-
s.homepage = "http://github.com/nathanstitt/liquid_assets"
|
41
|
-
s.licenses = ["MIT"]
|
42
|
-
s.require_paths = ["lib"]
|
43
|
-
s.rubygems_version = "1.8.23"
|
44
|
-
s.summary = "Liquid formmated views and assets"
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
45
20
|
|
46
|
-
|
47
|
-
|
21
|
+
spec.add_dependency 'liquid'
|
22
|
+
spec.add_dependency 'tilt'
|
23
|
+
spec.add_dependency 'execjs'
|
24
|
+
spec.add_dependency 'actionpack', '>=3.2'
|
48
25
|
|
49
|
-
|
50
|
-
|
51
|
-
s.add_runtime_dependency(%q<tilt>, [">= 0"])
|
52
|
-
s.add_runtime_dependency(%q<sprockets>, [">= 0"])
|
53
|
-
s.add_runtime_dependency(%q<execjs>, [">= 0"])
|
54
|
-
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
55
|
-
s.add_development_dependency(%q<bundler>, [">= 0"])
|
56
|
-
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
57
|
-
else
|
58
|
-
s.add_dependency(%q<liquid>, [">= 0"])
|
59
|
-
s.add_dependency(%q<tilt>, [">= 0"])
|
60
|
-
s.add_dependency(%q<sprockets>, [">= 0"])
|
61
|
-
s.add_dependency(%q<execjs>, [">= 0"])
|
62
|
-
s.add_dependency(%q<rdoc>, [">= 0"])
|
63
|
-
s.add_dependency(%q<bundler>, [">= 0"])
|
64
|
-
s.add_dependency(%q<jeweler>, [">= 0"])
|
65
|
-
end
|
66
|
-
else
|
67
|
-
s.add_dependency(%q<liquid>, [">= 0"])
|
68
|
-
s.add_dependency(%q<tilt>, [">= 0"])
|
69
|
-
s.add_dependency(%q<sprockets>, [">= 0"])
|
70
|
-
s.add_dependency(%q<execjs>, [">= 0"])
|
71
|
-
s.add_dependency(%q<rdoc>, [">= 0"])
|
72
|
-
s.add_dependency(%q<bundler>, [">= 0"])
|
73
|
-
s.add_dependency(%q<jeweler>, [">= 0"])
|
74
|
-
end
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
27
|
+
spec.add_development_dependency "rake"
|
75
28
|
end
|
76
|
-
|
data/test/helper.rb
CHANGED
data/test/test_liquid_assets.rb
CHANGED
@@ -9,18 +9,17 @@ class TestLiquidAssets < Test::Unit::TestCase
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_mime_type
|
12
|
-
assert_equal 'application/javascript', LiquidAssets::
|
12
|
+
assert_equal 'application/javascript', LiquidAssets::PipelineTemplateEngine.default_mime_type
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_tilt_engine_rendering
|
16
16
|
scope = make_scope '/myapp/app/assets/javascripts', 'path/to/template.mustache'
|
17
17
|
|
18
|
-
template = LiquidAssets::
|
18
|
+
template = LiquidAssets::PipelineTemplateEngine.new(scope.s_path) { "This is {{me}}" }
|
19
|
+
|
20
|
+
tmpl="LQT.Templates[\"path/to/template\"] = function(locals,filters){\n var $_tmpbuf, $_html = LQT._FNS.html, $_err = LQT._FNS.err,\n $_rethrow=LQT._FNS.rethrow, $_merge=LQT._FNS.merge,\n $_range=LQT._FNS.range, $_array=LQT._FNS.array;\n /* == Template Begin == */\nvar $_buf = '';\nvar $_line_num = 0;\n/* == define cycles == */\nvar $_cycle_next = function (n) {\nn.i++;\nif (n.i >= n.length) n.i = 0;\n}\n$_buf+=('This is ');\n$_line_num = 1;\n$_tmpbuf = locals.me;\n$_buf+=($_tmpbuf===null||typeof($_tmpbuf)===\"undefined\"?\"\":$_tmpbuf);\n return $_buf;\n }\n;"
|
21
|
+
assert_equal tmpl, template.render(scope, {})
|
19
22
|
|
20
|
-
assert_equal <<-END_EXPECTED, template.render(scope, {})
|
21
|
-
this.LQT || (this.LQT = {});
|
22
|
-
this.LQT[\"path/to/template\"] = function(locals,filters){\nvar $_tmpbuf, $_html = LQT._FNS.html, $_err = LQT._FNS.err, $_rethrow=LQT._FNS.rethrow, $_merge=LQT._FNS.merge, $_range=LQT._FNS.range, $_array=LQT._FNS.array;\n/* == Template Begin == */\nvar $_buf = '';\nvar $_line_num = 0;\n/* == define cycles == */\nvar $_cycle_next = function (n) {\nn.i++;\nif (n.i >= n.length) n.i = 0;\n}\n$_buf+=('This is ');\n$_line_num = 1;\n$_tmpbuf = locals.me;\n$_buf+=($_tmpbuf===null||typeof($_tmpbuf)===\"undefined\"?\"\":$_tmpbuf);\nreturn $_buf;\n};
|
23
|
-
END_EXPECTED
|
24
23
|
end
|
25
24
|
|
26
25
|
def test_template_rendering
|
@@ -30,4 +29,48 @@ class TestLiquidAssets < Test::Unit::TestCase
|
|
30
29
|
assert_equal 'foo=bar', template
|
31
30
|
end
|
32
31
|
|
32
|
+
|
33
|
+
def test_resolver
|
34
|
+
LiquidAssets::Config.content_provider = lambda do | path |
|
35
|
+
'good' == path ? 'Hello {{bob|upcase}}' : false
|
36
|
+
end
|
37
|
+
|
38
|
+
details = {:formats=>[:liquid], :locale=>[:en], :handlers=>[] }
|
39
|
+
resolver = LiquidAssets::Resolver.instance
|
40
|
+
|
41
|
+
assert_empty resolver.find_all('bad','',false, details )
|
42
|
+
assert_not_empty resolver.find_all('good','',false, details )
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_resolver_caches
|
46
|
+
times_called = 0
|
47
|
+
LiquidAssets::Config.content_provider = lambda do | path |
|
48
|
+
times_called += 1
|
49
|
+
'foo/bar/good' == path ? 'Hello {{bob|upcase}}' : false
|
50
|
+
end
|
51
|
+
|
52
|
+
details = {:formats=>[:liquid], :locale=>[:en], :handlers=>[] }
|
53
|
+
resolver = LiquidAssets::Resolver.instance
|
54
|
+
key = 'dumb-key'
|
55
|
+
(0...3).each do
|
56
|
+
resolver.find_all('foo/bar/good',nil ,false, details, key )
|
57
|
+
end
|
58
|
+
assert_equal 1, times_called
|
59
|
+
|
60
|
+
times_called = 0
|
61
|
+
|
62
|
+
resolver.clear_cache_for( 'bad' ) # shouldn't clear cache for 'good'
|
63
|
+
|
64
|
+
resolver.find_all('foo/bar/good',nil,false, details, key )
|
65
|
+
|
66
|
+
assert_equal 0, times_called
|
67
|
+
|
68
|
+
resolver.clear_cache_for( 'foo/bar/good' )
|
69
|
+
|
70
|
+
resolver.find_all('foo/bar/good',nil,false, details, key )
|
71
|
+
|
72
|
+
assert_equal 1, times_called
|
73
|
+
|
74
|
+
|
75
|
+
end
|
33
76
|
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
(function( namespace, document, undefined) {
|
2
|
+
|
3
|
+
namespace = namespace.<%= LiquidAssets::Config.namespace%> || (namespace.<%=LiquidAssets::Config.namespace%>={});
|
4
|
+
namespace.Templates || ( namespace.Templates={} );
|
5
|
+
|
6
|
+
namespace.Globals || ( namespace.Globals = {} );
|
7
|
+
var server_globals = <%= LiquidAssets::Config.globals.to_json.html_safe %>;
|
8
|
+
for ( var key in server_globals ){
|
9
|
+
namespace.Globals[ key ] = server_globals[key];
|
10
|
+
}
|
11
|
+
|
12
|
+
namespace.Render = function( name, data ){
|
13
|
+
if (data == null) {
|
14
|
+
data = {};
|
15
|
+
}
|
16
|
+
for ( var key in namespace.Globals )
|
17
|
+
if (! data.hasOwnProperty(key) )
|
18
|
+
data[key] = namespace.Globals[key];
|
19
|
+
|
20
|
+
return namespace.Templates[name]( data, namespace.Filters );
|
21
|
+
};
|
22
|
+
|
23
|
+
namespace.RenderPartial = function( name, data ){
|
24
|
+
var path, parts = name.split('/');
|
25
|
+
if ( parts.length > 1 )
|
26
|
+
path = parts.slice( 0, parts.length-1) +'/_'+parts[ parts.length-1 ];
|
27
|
+
else
|
28
|
+
path = '_' + parts[ parts.length-1 ];
|
29
|
+
return namespace.Render( path, data );
|
30
|
+
};
|
31
|
+
|
32
|
+
namespace._FNS = {
|
33
|
+
html: function (html) {
|
34
|
+
return html.replace(/\\/g, '\\\\')
|
35
|
+
.replace(/'/g, '\\\'')
|
36
|
+
.replace(/"/g, '\\\"')
|
37
|
+
.replace(/\r/g, '\\r')
|
38
|
+
.replace(/\n/g, '\\n');
|
39
|
+
},
|
40
|
+
err: function (msg) {
|
41
|
+
var html = '<pre style="font-family:Courier; font-weight:bold; \
|
42
|
+
font-size:14px; color:red; padding:4px 20px 4px 20px; border:1px solid #CCC; \
|
43
|
+
background-color:#FFF5F0; line-height:1.6em; white-space:pre-wrap; \
|
44
|
+
white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; \
|
45
|
+
word-wrap:break-word; z-index:9999">' + msg + '</pre>';
|
46
|
+
return html;
|
47
|
+
},
|
48
|
+
rethrow: function (err, filename) {
|
49
|
+
var msg = 'An error occurred while rendering\n' +
|
50
|
+
'Line: ' + $_line_num + (filename ? ' File: ' + filename : '') +
|
51
|
+
'\n ' + err;
|
52
|
+
$_buf += $_err(msg);
|
53
|
+
},
|
54
|
+
merge: function () {
|
55
|
+
var ret = {};
|
56
|
+
for (var i in arguments) {
|
57
|
+
var obj = arguments[i];
|
58
|
+
for (var j in obj) {
|
59
|
+
ret[j] = obj[j];
|
60
|
+
}
|
61
|
+
}
|
62
|
+
return ret;
|
63
|
+
},
|
64
|
+
range: function (s, e) {
|
65
|
+
s = parseInt(s);
|
66
|
+
e = parseInt(e);
|
67
|
+
var r = [];
|
68
|
+
if (isNaN(s) || isNaN(e)) return r;
|
69
|
+
for (; s <= e; s++) {
|
70
|
+
r.push(s);
|
71
|
+
}
|
72
|
+
return r;
|
73
|
+
},
|
74
|
+
'array': function (data) {
|
75
|
+
if (Array.isArray(data)) return data;
|
76
|
+
var ret = [];
|
77
|
+
for (var i in data) {
|
78
|
+
if (i !== 'size') {
|
79
|
+
ret.push(data[i]);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
return ret;
|
83
|
+
}
|
84
|
+
};
|
85
|
+
|
86
|
+
var toNumber = function(input) {
|
87
|
+
return Number(input);
|
88
|
+
};
|
89
|
+
|
90
|
+
var toString = function(input) {
|
91
|
+
if (!input) {
|
92
|
+
return '';
|
93
|
+
}
|
94
|
+
if (_.isString(input)) {
|
95
|
+
return input;
|
96
|
+
} else if (typeof input.toString === "function") {
|
97
|
+
return input.toString();
|
98
|
+
} else {
|
99
|
+
return Object.prototype.toString.call(input);
|
100
|
+
}
|
101
|
+
};
|
102
|
+
|
103
|
+
namespace.Filters = {
|
104
|
+
size: function(input) {
|
105
|
+
return input.length;
|
106
|
+
},
|
107
|
+
downcase: function(input) {
|
108
|
+
return toString(input).toLowerCase();
|
109
|
+
},
|
110
|
+
upcase: function(input) {
|
111
|
+
return toString(input).toUpperCase();
|
112
|
+
},
|
113
|
+
append: function(input, other) {
|
114
|
+
return [toString(input), toString(other)].join();
|
115
|
+
},
|
116
|
+
prepend: function(input, other) {
|
117
|
+
return [toString(other), toString(input)].join();
|
118
|
+
},
|
119
|
+
empty: function(input) {
|
120
|
+
if (!input) {
|
121
|
+
return true;
|
122
|
+
}
|
123
|
+
if (input.length == null) {
|
124
|
+
return false;
|
125
|
+
}
|
126
|
+
return true;
|
127
|
+
},
|
128
|
+
truncate: function(input, length, truncateString) {
|
129
|
+
var l;
|
130
|
+
|
131
|
+
if (length == null) {
|
132
|
+
length = 50;
|
133
|
+
}
|
134
|
+
if (truncateString == null) {
|
135
|
+
truncateString = '...';
|
136
|
+
}
|
137
|
+
input = toString(input);
|
138
|
+
truncateString = toString(truncateString);
|
139
|
+
if (input == null) {
|
140
|
+
return '';
|
141
|
+
}
|
142
|
+
if (!input.slice) {
|
143
|
+
return '';
|
144
|
+
}
|
145
|
+
length = toNumber(length);
|
146
|
+
l = length - truncateString.length;
|
147
|
+
if (l < 0) {
|
148
|
+
l = 0;
|
149
|
+
}
|
150
|
+
if (input.length > length) {
|
151
|
+
return input.slice(0, +l + 1 || 9e9) + truncateString;
|
152
|
+
} else {
|
153
|
+
return input;
|
154
|
+
}
|
155
|
+
},
|
156
|
+
truncatewords: function(input, words, truncateString) {
|
157
|
+
var l, wordlist;
|
158
|
+
|
159
|
+
if (words == null) {
|
160
|
+
words = 15;
|
161
|
+
}
|
162
|
+
if (truncateString == null) {
|
163
|
+
truncateString = '...';
|
164
|
+
}
|
165
|
+
input = toString(input);
|
166
|
+
if (input == null) {
|
167
|
+
return '';
|
168
|
+
}
|
169
|
+
if (!input.slice) {
|
170
|
+
return '';
|
171
|
+
}
|
172
|
+
wordlist = input.split(" ");
|
173
|
+
words = toNumber(words);
|
174
|
+
l = words - 1;
|
175
|
+
if (l < 0) {
|
176
|
+
l = 0;
|
177
|
+
}
|
178
|
+
if (wordlist.length > l) {
|
179
|
+
return wordlist.slice(0, +l + 1 || 9e9).join(" ") + truncateString;
|
180
|
+
} else {
|
181
|
+
return input;
|
182
|
+
}
|
183
|
+
},
|
184
|
+
split: function(input, pattern) {
|
185
|
+
input = toString(input);
|
186
|
+
if (!input) {
|
187
|
+
return null;
|
188
|
+
}
|
189
|
+
return input.split(pattern);
|
190
|
+
},
|
191
|
+
join: function(input, glue) {
|
192
|
+
if (glue == null) {
|
193
|
+
glue = ' ';
|
194
|
+
}
|
195
|
+
return _(input).flatten().join(glue);
|
196
|
+
},
|
197
|
+
first: function(array) {
|
198
|
+
if (array.length > 0) {
|
199
|
+
return array[0];
|
200
|
+
} else {
|
201
|
+
return null;
|
202
|
+
}
|
203
|
+
},
|
204
|
+
last: function(array) {
|
205
|
+
if (array.length > 0) {
|
206
|
+
return array[array.length - 1];
|
207
|
+
} else {
|
208
|
+
return null;
|
209
|
+
}
|
210
|
+
},
|
211
|
+
plus: function(input, operand) {
|
212
|
+
return toNumber(input) + toNumber(operand);
|
213
|
+
},
|
214
|
+
minus: function(input, operand) {
|
215
|
+
return toNumber(input) - toNumber(operand);
|
216
|
+
},
|
217
|
+
times: function(input, operand) {
|
218
|
+
return toNumber(input) * toNumber(operand);
|
219
|
+
},
|
220
|
+
dividedBy: function(input, operand) {
|
221
|
+
return toNumber(input) / toNumber(operand);
|
222
|
+
},
|
223
|
+
modulo: function(input, operand) {
|
224
|
+
return toNumber(input) % toNumber(operand);
|
225
|
+
}
|
226
|
+
};
|
227
|
+
|
228
|
+
}(window, document));
|
data/vendor/tinyliquid.js
CHANGED
@@ -2139,11 +2139,14 @@ exports.tags = function (text, start, context) {
|
|
2139
2139
|
setLineNumber();
|
2140
2140
|
var include_parts = inc_tag.name.split('/');
|
2141
2141
|
include_parts[ include_parts.length-1] = '_' + include_parts[ include_parts.length - 1 ];
|
2142
|
-
var partial_function = context.
|
2143
|
-
script += 'if ( ' + partial_function + '){'
|
2144
|
-
|
2145
|
-
|
2146
|
-
|
2142
|
+
var partial_function = context.namespace + '.Templates[\'' + include_parts.join('/') + '\']';
|
2143
|
+
script += 'if ( ' + partial_function + '){';
|
2144
|
+
if ( inc_tag.with )
|
2145
|
+
script+=' $_buf+=' + partial_function + '({\'' + inc_tag.with + '\': locals.' + (inc_tag.with) + '},filters);';
|
2146
|
+
else
|
2147
|
+
script+=' $_buf+=' + partial_function + '(locals,filters);';
|
2148
|
+
script+='} else {' +
|
2149
|
+
' $_buf+=\'Partial not found: ' + include_parts.join('/') + '\';' +
|
2147
2150
|
'}';
|
2148
2151
|
// script += '/* === include "' + inc_tag.name + '"' +
|
2149
2152
|
// (inc_tag.with ? ' with "' + inc_tag.with + '"' : '') +
|
@@ -2224,7 +2227,7 @@ exports.parse = function (text, options) {
|
|
2224
2227
|
};
|
2225
2228
|
|
2226
2229
|
// 初始化编译环境
|
2227
|
-
context.
|
2230
|
+
context.namespace = options.namespace;
|
2228
2231
|
context.customTags = options.tags; // 自定义的标记解析
|
2229
2232
|
context.loop = 0; // { 嵌套层数
|
2230
2233
|
context.loopName = []; // 当前嵌套标记名称
|