ejs-rcompiler 0.1.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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Ben Curren
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 PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL 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.
data/README.rdoc ADDED
@@ -0,0 +1,39 @@
1
+ = ejs-rcompiler
2
+
3
+ A simple ruby compiler that compiles an ejs file to js. Here is an example usage:
4
+
5
+ == In file named or.ui.Cities.ejs
6
+ <ul>
7
+ <%# Loop through each city %>
8
+ <% $.each(cities, function() { %>
9
+ <li><%= this.name %></li>
10
+ <% }) %>
11
+ </ul>
12
+
13
+ == Compile the file like so
14
+ compiler = Ejs::Compiler.new
15
+ compiler.compile(ejs_file)
16
+
17
+ This will create a .js file with a JavaScript class name that matches the file name.
18
+
19
+ == You can then require the output js file as such.
20
+ <script src="/javascripts/or.ui.Cities.js"></script>
21
+ <script>
22
+ document.body.innerHTML = or.ui.Cities.template({
23
+ cities: [{ name: "Paris" }, { name: "Mountain View" }, { name: "Las Vegas" }]
24
+ });
25
+ </script>
26
+
27
+ == Note on Patches/Pull Requests
28
+
29
+ * Fork the project.
30
+ * Make your feature addition or bug fix.
31
+ * Add tests for it. This is important so I don't break it in a
32
+ future version unintentionally.
33
+ * Commit, do not mess with rakefile, version, or history.
34
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
35
+ * Send me a pull request. Bonus points for topic branches.
36
+
37
+ == Copyright
38
+
39
+ Copyright (c) 2010 Ben Curren. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ejs-rcompiler"
8
+ gem.summary = %Q{Ejs to Javascript compiler written in Ruby. This allows you to define HTML heavy Javascript components in EJS and compile them down to Javascript functions for later use.}
9
+ gem.description = %Q{Ejs to Javascript compiler.}
10
+ gem.email = "ben@outright.com"
11
+ gem.homepage = "http://github.com/bcurren/ejs-rcompiler"
12
+ gem.authors = ["Ben Curren"]
13
+ gem.add_dependency "treetop", ">= 1.4.0"
14
+ gem.add_development_dependency "rspec", ">= 1.2.9"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+
36
+ task :default => :spec
37
+
38
+ require 'rake/rdoctask'
39
+ Rake::RDocTask.new do |rdoc|
40
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = "ejs-rcompiler #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
47
+
48
+ PARSER_FILE="lib/ejs/grammar.rb"
49
+ GRAMMAR_FILE="lib/ejs/grammar.treetop"
50
+
51
+ namespace :treetop do
52
+ desc "Generate parser from treetop grammar."
53
+ task :generate => PARSER_FILE
54
+ end
55
+
56
+ file PARSER_FILE => GRAMMAR_FILE do |t|
57
+ require 'treetop'
58
+ compiler = Treetop::Compiler::GrammarCompiler.new
59
+ compiler.compile(t.prerequisites[0], t.name)
60
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec" }
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ejs-rcompiler}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ben Curren"]
12
+ s.date = %q{2010-09-27}
13
+ s.description = %q{Ejs to Javascript compiler.}
14
+ s.email = %q{ben@outright.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "autotest/discover.rb",
27
+ "ejs-rcompiler.gemspec",
28
+ "lib/ejs-rcompiler.rb",
29
+ "lib/ejs/compiler.rb",
30
+ "lib/ejs/grammar.rb",
31
+ "lib/ejs/grammar.treetop",
32
+ "lib/ejs/parse_error.rb",
33
+ "lib/ejs/parser.rb",
34
+ "spec/compiler_spec.rb",
35
+ "spec/fixtures/Cities.ejs",
36
+ "spec/fixtures/Cities.js",
37
+ "spec/parser_spec.rb",
38
+ "spec/spec.opts",
39
+ "spec/spec_helper.rb"
40
+ ]
41
+ s.homepage = %q{http://github.com/bcurren/ejs-rcompiler}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.6}
45
+ s.summary = %q{Ejs to Javascript compiler written in Ruby. This allows you to define HTML heavy Javascript components in EJS and compile them down to Javascript functions for later use.}
46
+ s.test_files = [
47
+ "spec/compiler_spec.rb",
48
+ "spec/parser_spec.rb",
49
+ "spec/spec_helper.rb"
50
+ ]
51
+
52
+ if s.respond_to? :specification_version then
53
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<treetop>, [">= 1.4.0"])
58
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
59
+ else
60
+ s.add_dependency(%q<treetop>, [">= 1.4.0"])
61
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
62
+ end
63
+ else
64
+ s.add_dependency(%q<treetop>, [">= 1.4.0"])
65
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
66
+ end
67
+ end
68
+
@@ -0,0 +1,91 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/parser")
2
+ require File.expand_path("#{File.dirname(__FILE__)}/parse_error")
3
+
4
+ module Ejs
5
+ AUTOGENERATED = "// Autogenerated from an Ejs file. Edits may be lost.\n"
6
+
7
+ class Compiler
8
+ def initialize
9
+ @parser = Parser.new
10
+ end
11
+
12
+ def compile(source_path, namespace = nil, target_path = source_path.gsub(/\.ejs\Z/, '.js'))
13
+ File.open(target_path, 'w') do |target_file|
14
+ target_file.write(AUTOGENERATED+"\n\n")
15
+ target_file.write(js_source(source_path, namespace))
16
+ end
17
+ end
18
+
19
+ # compile a ejs file into javascript
20
+ def js_source(source_path, namespace = nil, output_as_array = false)
21
+ template_name = File.basename(source_path, ".ejs")
22
+ template_name = "#{namespace}.#{template_name}" if namespace
23
+
24
+ js_source_from_string(template_name, File.read(source_path), output_as_array)
25
+ end
26
+
27
+ # compile a string containing ejs source into javascript
28
+ def js_source_from_string(template_name, content, output_as_array = false)
29
+ buffer = []
30
+ parsed = @parser.parse(content)
31
+ if parsed.nil?
32
+ raise ParseError.new(@parser.failure_reason, @parser.failure_line, @parser.failure_column)
33
+ end
34
+
35
+ template_namespace(buffer, template_name)
36
+ template_header(buffer, template_name)
37
+ parsed.elements.each do |element|
38
+ push_content(buffer, element)
39
+ end
40
+ template_footer(buffer)
41
+
42
+ output_as_array ? buffer : buffer.join("\n")
43
+ end
44
+
45
+ private
46
+
47
+ def template_namespace(buffer, template_name)
48
+ ns = template_name.split(".")
49
+ ns_str = "window"
50
+ begin
51
+ ns_str = "#{ns_str}.#{ns.shift}"
52
+ buffer.push("#{ns_str} = #{ns_str} || {};")
53
+ end until ns.empty?
54
+ end
55
+
56
+ def template_header(buffer, template_name)
57
+ buffer.push("#{template_name}.template = function(options) {")
58
+ buffer.push(" var p = [];")
59
+ buffer.push(" with(options) {")
60
+ end
61
+
62
+ def push_content(buffer, element)
63
+ case element.node_type
64
+ when 'static_content'
65
+ buffer.push(" p.push('#{escape(element.text_value)}');")
66
+ when 'scriplet'
67
+ buffer.push(" #{element.text_value.strip}")
68
+ when 'expression'
69
+ buffer.push(" p.push(#{element.text_value.strip});")
70
+ when 'comment'
71
+ buffer.push(" /*#{element.text_value}*/")
72
+ else
73
+ raise "Unknown node_type encountered while parsing: #{element.node_type}"
74
+ end
75
+ end
76
+
77
+ def template_footer(buffer)
78
+ buffer.push(" }")
79
+ buffer.push(" return p.join('');")
80
+ buffer.push("}")
81
+ end
82
+
83
+ def escape(content)
84
+ content.
85
+ gsub(/\n/, '\n').
86
+ gsub(/\t/, '\t').
87
+ gsub(/"/, '\"').
88
+ gsub(/'/, "\\\\'") # yes, this is how you escape a single quote
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,658 @@
1
+ # Autogenerated from a Treetop grammar. Edits may be lost.
2
+
3
+
4
+ module Ejs
5
+ module Grammar
6
+ include Treetop::Runtime
7
+
8
+ def root
9
+ @root ||= :program
10
+ end
11
+
12
+ def _nt_program
13
+ start_index = index
14
+ if node_cache[:program].has_key?(index)
15
+ cached = node_cache[:program][index]
16
+ if cached
17
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
18
+ @index = cached.interval.end
19
+ end
20
+ return cached
21
+ end
22
+
23
+ s0, i0 = [], index
24
+ loop do
25
+ i1 = index
26
+ r2 = _nt_expression
27
+ if r2
28
+ r1 = r2
29
+ else
30
+ r3 = _nt_comment
31
+ if r3
32
+ r1 = r3
33
+ else
34
+ r4 = _nt_scriplet
35
+ if r4
36
+ r1 = r4
37
+ else
38
+ r5 = _nt_static_content
39
+ if r5
40
+ r1 = r5
41
+ else
42
+ @index = i1
43
+ r1 = nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+ if r1
49
+ s0 << r1
50
+ else
51
+ break
52
+ end
53
+ end
54
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
55
+
56
+ node_cache[:program][start_index] = r0
57
+
58
+ r0
59
+ end
60
+
61
+ module Expression0
62
+ def script_content
63
+ elements[1]
64
+ end
65
+
66
+ end
67
+
68
+ module Expression1
69
+ def node_type
70
+ "expression"
71
+ end
72
+
73
+ def text_value
74
+ script_content.text_value
75
+ end
76
+ end
77
+
78
+ def _nt_expression
79
+ start_index = index
80
+ if node_cache[:expression].has_key?(index)
81
+ cached = node_cache[:expression][index]
82
+ if cached
83
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
84
+ @index = cached.interval.end
85
+ end
86
+ return cached
87
+ end
88
+
89
+ i0, s0 = index, []
90
+ if has_terminal?('<%=', false, index)
91
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 3))
92
+ @index += 3
93
+ else
94
+ terminal_parse_failure('<%=')
95
+ r1 = nil
96
+ end
97
+ s0 << r1
98
+ if r1
99
+ r2 = _nt_script_content
100
+ s0 << r2
101
+ if r2
102
+ if has_terminal?('%>', false, index)
103
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
104
+ @index += 2
105
+ else
106
+ terminal_parse_failure('%>')
107
+ r3 = nil
108
+ end
109
+ s0 << r3
110
+ end
111
+ end
112
+ if s0.last
113
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
114
+ r0.extend(Expression0)
115
+ r0.extend(Expression1)
116
+ else
117
+ @index = i0
118
+ r0 = nil
119
+ end
120
+
121
+ node_cache[:expression][start_index] = r0
122
+
123
+ r0
124
+ end
125
+
126
+ module Comment0
127
+ def script_content
128
+ elements[1]
129
+ end
130
+
131
+ end
132
+
133
+ module Comment1
134
+ def node_type
135
+ "comment"
136
+ end
137
+
138
+ def text_value
139
+ script_content.text_value
140
+ end
141
+ end
142
+
143
+ def _nt_comment
144
+ start_index = index
145
+ if node_cache[:comment].has_key?(index)
146
+ cached = node_cache[:comment][index]
147
+ if cached
148
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
149
+ @index = cached.interval.end
150
+ end
151
+ return cached
152
+ end
153
+
154
+ i0, s0 = index, []
155
+ if has_terminal?('<%#', false, index)
156
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 3))
157
+ @index += 3
158
+ else
159
+ terminal_parse_failure('<%#')
160
+ r1 = nil
161
+ end
162
+ s0 << r1
163
+ if r1
164
+ r2 = _nt_script_content
165
+ s0 << r2
166
+ if r2
167
+ if has_terminal?('%>', false, index)
168
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
169
+ @index += 2
170
+ else
171
+ terminal_parse_failure('%>')
172
+ r3 = nil
173
+ end
174
+ s0 << r3
175
+ end
176
+ end
177
+ if s0.last
178
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
179
+ r0.extend(Comment0)
180
+ r0.extend(Comment1)
181
+ else
182
+ @index = i0
183
+ r0 = nil
184
+ end
185
+
186
+ node_cache[:comment][start_index] = r0
187
+
188
+ r0
189
+ end
190
+
191
+ module Scriplet0
192
+ def script_content
193
+ elements[1]
194
+ end
195
+
196
+ end
197
+
198
+ module Scriplet1
199
+ def node_type
200
+ "scriplet"
201
+ end
202
+
203
+ def text_value
204
+ script_content.text_value
205
+ end
206
+ end
207
+
208
+ def _nt_scriplet
209
+ start_index = index
210
+ if node_cache[:scriplet].has_key?(index)
211
+ cached = node_cache[:scriplet][index]
212
+ if cached
213
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
214
+ @index = cached.interval.end
215
+ end
216
+ return cached
217
+ end
218
+
219
+ i0, s0 = index, []
220
+ if has_terminal?('<%', false, index)
221
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 2))
222
+ @index += 2
223
+ else
224
+ terminal_parse_failure('<%')
225
+ r1 = nil
226
+ end
227
+ s0 << r1
228
+ if r1
229
+ r2 = _nt_script_content
230
+ s0 << r2
231
+ if r2
232
+ if has_terminal?('%>', false, index)
233
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
234
+ @index += 2
235
+ else
236
+ terminal_parse_failure('%>')
237
+ r3 = nil
238
+ end
239
+ s0 << r3
240
+ end
241
+ end
242
+ if s0.last
243
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
244
+ r0.extend(Scriplet0)
245
+ r0.extend(Scriplet1)
246
+ else
247
+ @index = i0
248
+ r0 = nil
249
+ end
250
+
251
+ node_cache[:scriplet][start_index] = r0
252
+
253
+ r0
254
+ end
255
+
256
+ module StaticContent0
257
+ def node_type
258
+ "static_content"
259
+ end
260
+
261
+ def text_value
262
+ elements.map do |element|
263
+ element.text_value
264
+ end.join('')
265
+ end
266
+ end
267
+
268
+ def _nt_static_content
269
+ start_index = index
270
+ if node_cache[:static_content].has_key?(index)
271
+ cached = node_cache[:static_content][index]
272
+ if cached
273
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
274
+ @index = cached.interval.end
275
+ end
276
+ return cached
277
+ end
278
+
279
+ s0, i0 = [], index
280
+ loop do
281
+ i1 = index
282
+ r2 = _nt_right_delimeter_escaped
283
+ if r2
284
+ r1 = r2
285
+ else
286
+ r3 = _nt_left_delimeter_escaped
287
+ if r3
288
+ r1 = r3
289
+ else
290
+ r4 = _nt_unescaped_static_content
291
+ if r4
292
+ r1 = r4
293
+ else
294
+ @index = i1
295
+ r1 = nil
296
+ end
297
+ end
298
+ end
299
+ if r1
300
+ s0 << r1
301
+ else
302
+ break
303
+ end
304
+ end
305
+ if s0.empty?
306
+ @index = i0
307
+ r0 = nil
308
+ else
309
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
310
+ r0.extend(StaticContent0)
311
+ end
312
+
313
+ node_cache[:static_content][start_index] = r0
314
+
315
+ r0
316
+ end
317
+
318
+ module ScriptContent0
319
+ def text_value
320
+ elements.map do |element|
321
+ element.text_value
322
+ end.join('')
323
+ end
324
+ end
325
+
326
+ def _nt_script_content
327
+ start_index = index
328
+ if node_cache[:script_content].has_key?(index)
329
+ cached = node_cache[:script_content][index]
330
+ if cached
331
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
332
+ @index = cached.interval.end
333
+ end
334
+ return cached
335
+ end
336
+
337
+ s0, i0 = [], index
338
+ loop do
339
+ i1 = index
340
+ r2 = _nt_right_delimeter_escaped
341
+ if r2
342
+ r1 = r2
343
+ else
344
+ r3 = _nt_left_delimeter_escaped
345
+ if r3
346
+ r1 = r3
347
+ else
348
+ r4 = _nt_unescape_script_content
349
+ if r4
350
+ r1 = r4
351
+ else
352
+ @index = i1
353
+ r1 = nil
354
+ end
355
+ end
356
+ end
357
+ if r1
358
+ s0 << r1
359
+ else
360
+ break
361
+ end
362
+ end
363
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
364
+ r0.extend(ScriptContent0)
365
+
366
+ node_cache[:script_content][start_index] = r0
367
+
368
+ r0
369
+ end
370
+
371
+ module RightDelimeterEscaped0
372
+ def text_value
373
+ '%>'
374
+ end
375
+ end
376
+
377
+ def _nt_right_delimeter_escaped
378
+ start_index = index
379
+ if node_cache[:right_delimeter_escaped].has_key?(index)
380
+ cached = node_cache[:right_delimeter_escaped][index]
381
+ if cached
382
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
383
+ @index = cached.interval.end
384
+ end
385
+ return cached
386
+ end
387
+
388
+ if has_terminal?('%%>', false, index)
389
+ r0 = instantiate_node(SyntaxNode,input, index...(index + 3))
390
+ r0.extend(RightDelimeterEscaped0)
391
+ @index += 3
392
+ else
393
+ terminal_parse_failure('%%>')
394
+ r0 = nil
395
+ end
396
+
397
+ node_cache[:right_delimeter_escaped][start_index] = r0
398
+
399
+ r0
400
+ end
401
+
402
+ module LeftDelimeterEscaped0
403
+ def text_value
404
+ '<%'
405
+ end
406
+ end
407
+
408
+ def _nt_left_delimeter_escaped
409
+ start_index = index
410
+ if node_cache[:left_delimeter_escaped].has_key?(index)
411
+ cached = node_cache[:left_delimeter_escaped][index]
412
+ if cached
413
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
414
+ @index = cached.interval.end
415
+ end
416
+ return cached
417
+ end
418
+
419
+ if has_terminal?('<%%', false, index)
420
+ r0 = instantiate_node(SyntaxNode,input, index...(index + 3))
421
+ r0.extend(LeftDelimeterEscaped0)
422
+ @index += 3
423
+ else
424
+ terminal_parse_failure('<%%')
425
+ r0 = nil
426
+ end
427
+
428
+ node_cache[:left_delimeter_escaped][start_index] = r0
429
+
430
+ r0
431
+ end
432
+
433
+ module UnescapedStaticContent0
434
+ end
435
+
436
+ def _nt_unescaped_static_content
437
+ start_index = index
438
+ if node_cache[:unescaped_static_content].has_key?(index)
439
+ cached = node_cache[:unescaped_static_content][index]
440
+ if cached
441
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
442
+ @index = cached.interval.end
443
+ end
444
+ return cached
445
+ end
446
+
447
+ s0, i0 = [], index
448
+ loop do
449
+ i1, s1 = index, []
450
+ i2 = index
451
+ if has_terminal?('<%%', false, index)
452
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 3))
453
+ @index += 3
454
+ else
455
+ terminal_parse_failure('<%%')
456
+ r3 = nil
457
+ end
458
+ if r3
459
+ r2 = nil
460
+ else
461
+ @index = i2
462
+ r2 = instantiate_node(SyntaxNode,input, index...index)
463
+ end
464
+ s1 << r2
465
+ if r2
466
+ i4 = index
467
+ if has_terminal?('%%>', false, index)
468
+ r5 = instantiate_node(SyntaxNode,input, index...(index + 3))
469
+ @index += 3
470
+ else
471
+ terminal_parse_failure('%%>')
472
+ r5 = nil
473
+ end
474
+ if r5
475
+ r4 = nil
476
+ else
477
+ @index = i4
478
+ r4 = instantiate_node(SyntaxNode,input, index...index)
479
+ end
480
+ s1 << r4
481
+ if r4
482
+ i6 = index
483
+ if has_terminal?('<%', false, index)
484
+ r7 = instantiate_node(SyntaxNode,input, index...(index + 2))
485
+ @index += 2
486
+ else
487
+ terminal_parse_failure('<%')
488
+ r7 = nil
489
+ end
490
+ if r7
491
+ r6 = nil
492
+ else
493
+ @index = i6
494
+ r6 = instantiate_node(SyntaxNode,input, index...index)
495
+ end
496
+ s1 << r6
497
+ if r6
498
+ i8 = index
499
+ if has_terminal?('%>', false, index)
500
+ r9 = instantiate_node(SyntaxNode,input, index...(index + 2))
501
+ @index += 2
502
+ else
503
+ terminal_parse_failure('%>')
504
+ r9 = nil
505
+ end
506
+ if r9
507
+ r8 = nil
508
+ else
509
+ @index = i8
510
+ r8 = instantiate_node(SyntaxNode,input, index...index)
511
+ end
512
+ s1 << r8
513
+ if r8
514
+ if index < input_length
515
+ r10 = instantiate_node(SyntaxNode,input, index...(index + 1))
516
+ @index += 1
517
+ else
518
+ terminal_parse_failure("any character")
519
+ r10 = nil
520
+ end
521
+ s1 << r10
522
+ end
523
+ end
524
+ end
525
+ end
526
+ if s1.last
527
+ r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
528
+ r1.extend(UnescapedStaticContent0)
529
+ else
530
+ @index = i1
531
+ r1 = nil
532
+ end
533
+ if r1
534
+ s0 << r1
535
+ else
536
+ break
537
+ end
538
+ end
539
+ if s0.empty?
540
+ @index = i0
541
+ r0 = nil
542
+ else
543
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
544
+ end
545
+
546
+ node_cache[:unescaped_static_content][start_index] = r0
547
+
548
+ r0
549
+ end
550
+
551
+ module UnescapeScriptContent0
552
+ end
553
+
554
+ def _nt_unescape_script_content
555
+ start_index = index
556
+ if node_cache[:unescape_script_content].has_key?(index)
557
+ cached = node_cache[:unescape_script_content][index]
558
+ if cached
559
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
560
+ @index = cached.interval.end
561
+ end
562
+ return cached
563
+ end
564
+
565
+ s0, i0 = [], index
566
+ loop do
567
+ i1, s1 = index, []
568
+ i2 = index
569
+ if has_terminal?('%>', false, index)
570
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
571
+ @index += 2
572
+ else
573
+ terminal_parse_failure('%>')
574
+ r3 = nil
575
+ end
576
+ if r3
577
+ r2 = nil
578
+ else
579
+ @index = i2
580
+ r2 = instantiate_node(SyntaxNode,input, index...index)
581
+ end
582
+ s1 << r2
583
+ if r2
584
+ i4 = index
585
+ if has_terminal?('%%>', false, index)
586
+ r5 = instantiate_node(SyntaxNode,input, index...(index + 3))
587
+ @index += 3
588
+ else
589
+ terminal_parse_failure('%%>')
590
+ r5 = nil
591
+ end
592
+ if r5
593
+ r4 = nil
594
+ else
595
+ @index = i4
596
+ r4 = instantiate_node(SyntaxNode,input, index...index)
597
+ end
598
+ s1 << r4
599
+ if r4
600
+ i6 = index
601
+ if has_terminal?('<%%', false, index)
602
+ r7 = instantiate_node(SyntaxNode,input, index...(index + 3))
603
+ @index += 3
604
+ else
605
+ terminal_parse_failure('<%%')
606
+ r7 = nil
607
+ end
608
+ if r7
609
+ r6 = nil
610
+ else
611
+ @index = i6
612
+ r6 = instantiate_node(SyntaxNode,input, index...index)
613
+ end
614
+ s1 << r6
615
+ if r6
616
+ if index < input_length
617
+ r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
618
+ @index += 1
619
+ else
620
+ terminal_parse_failure("any character")
621
+ r8 = nil
622
+ end
623
+ s1 << r8
624
+ end
625
+ end
626
+ end
627
+ if s1.last
628
+ r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
629
+ r1.extend(UnescapeScriptContent0)
630
+ else
631
+ @index = i1
632
+ r1 = nil
633
+ end
634
+ if r1
635
+ s0 << r1
636
+ else
637
+ break
638
+ end
639
+ end
640
+ if s0.empty?
641
+ @index = i0
642
+ r0 = nil
643
+ else
644
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
645
+ end
646
+
647
+ node_cache[:unescape_script_content][start_index] = r0
648
+
649
+ r0
650
+ end
651
+
652
+ end
653
+
654
+ class GrammarParser < Treetop::Runtime::CompiledParser
655
+ include Grammar
656
+ end
657
+
658
+ end
@@ -0,0 +1,91 @@
1
+ module Ejs
2
+ grammar Grammar
3
+ rule program
4
+ (expression / comment / scriplet / static_content)*
5
+ end
6
+
7
+ rule expression
8
+ '<%=' script_content '%>' {
9
+ def node_type
10
+ "expression"
11
+ end
12
+
13
+ def text_value
14
+ script_content.text_value
15
+ end
16
+ }
17
+ end
18
+
19
+ rule comment
20
+ '<%#' script_content '%>' {
21
+ def node_type
22
+ "comment"
23
+ end
24
+
25
+ def text_value
26
+ script_content.text_value
27
+ end
28
+ }
29
+ end
30
+
31
+ rule scriplet
32
+ '<%' script_content '%>' {
33
+ def node_type
34
+ "scriplet"
35
+ end
36
+
37
+ def text_value
38
+ script_content.text_value
39
+ end
40
+ }
41
+ end
42
+
43
+ rule static_content
44
+ (right_delimeter_escaped / left_delimeter_escaped / unescaped_static_content)+ {
45
+ def node_type
46
+ "static_content"
47
+ end
48
+
49
+ def text_value
50
+ elements.map do |element|
51
+ element.text_value
52
+ end.join('')
53
+ end
54
+ }
55
+ end
56
+
57
+ rule script_content
58
+ (right_delimeter_escaped / left_delimeter_escaped / unescape_script_content)* {
59
+ def text_value
60
+ elements.map do |element|
61
+ element.text_value
62
+ end.join('')
63
+ end
64
+ }
65
+ end
66
+
67
+ rule right_delimeter_escaped
68
+ '%%>' {
69
+ def text_value
70
+ '%>'
71
+ end
72
+ }
73
+ end
74
+
75
+ rule left_delimeter_escaped
76
+ '<%%' {
77
+ def text_value
78
+ '<%'
79
+ end
80
+ }
81
+ end
82
+
83
+ rule unescaped_static_content
84
+ (!'<%%' !'%%>' !'<%' !'%>' .)+
85
+ end
86
+
87
+ rule unescape_script_content
88
+ (!'%>' !'%%>' !'<%%' .)+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,11 @@
1
+ module Ejs
2
+ class ParseError < StandardError
3
+ attr_reader :failure_reason, :failure_line, :failure_column
4
+ def initialize(failure_reason, failure_line, failure_column)
5
+ @failure_reason = failure_reason
6
+ @failure_line = failure_line
7
+ @failure_column = failure_column
8
+ super(@failure_reason)
9
+ end
10
+ end
11
+ end
data/lib/ejs/parser.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'treetop'
2
+ require File.expand_path("#{File.dirname(__FILE__)}/grammar")
3
+
4
+ module Ejs
5
+ class Parser < Treetop::Runtime::CompiledParser
6
+ include ::Ejs::Grammar
7
+ end
8
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/ejs/compiler")
@@ -0,0 +1,70 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'tempfile'
3
+ require 'fileutils'
4
+
5
+ describe "Compiler" do
6
+ before(:each) do
7
+ @compiler = Ejs::Compiler.new
8
+ end
9
+
10
+ it "should js_source_from_string function header and footer" do
11
+ compiled = @compiler.js_source_from_string("TemplateName", "", true)
12
+
13
+ compiled[0].should == "window.TemplateName = window.TemplateName || {};"
14
+ compiled[1].should == "TemplateName.template = function(options) {"
15
+ compiled[2].should == " var p = [];"
16
+ compiled[3].should == " with(options) {"
17
+
18
+ compiled[-3].should == " }"
19
+ compiled[-2].should == " return p.join('');"
20
+ compiled[-1].should == "}"
21
+ end
22
+
23
+ it "should add namespace declaration" do
24
+ compiled = @compiler.js_source_from_string("my.namespace.MyClass", "", true)
25
+ compiled[0].should == "window.my = window.my || {};"
26
+ compiled[1].should == "window.my.namespace = window.my.namespace || {};"
27
+ compiled[2].should == "window.my.namespace.MyClass = window.my.namespace.MyClass || {};"
28
+ compiled[3].should == "my.namespace.MyClass.template = function(options) {"
29
+ end
30
+
31
+ it "should js_source_from_string static content" do
32
+ compiled = @compiler.js_source_from_string("TemplateName", "<p>Static output</p>", true)
33
+ compiled[4].should == " p.push('<p>Static output</p>');"
34
+ end
35
+
36
+ it "should escape static content" do
37
+ compiled = @compiler.js_source_from_string("TemplateName", "\"\'\n\t", true)
38
+ compiled[4].should == " p.push('\\\"\\\'\\n\\t');"
39
+ end
40
+
41
+ it "should js_source_from_string scriplet" do
42
+ compiled = @compiler.js_source_from_string("TemplateName", "<% console.log('test') %>", true)
43
+ compiled[4].should == " console.log('test')"
44
+ end
45
+
46
+ it "should js_source_from_string expression" do
47
+ compiled = @compiler.js_source_from_string("TemplateName", "<%= this.name %>", true)
48
+ compiled[4].should == " p.push(this.name);"
49
+ end
50
+
51
+ it "should js_source_from_string comments" do
52
+ compiled = @compiler.js_source_from_string("TemplateName", "<%# This is a comment %>", true)
53
+ compiled[4].should == " /* This is a comment */"
54
+ end
55
+
56
+ it "should raise exception when syntax is invalid" do
57
+ lambda{ @compiler.js_source_from_string("Name", "<%= this.name %> %>") }.should raise_error(::Ejs::ParseError)
58
+ end
59
+
60
+ it "should compile a source file to a target file" do
61
+ fixtures = "#{File.dirname(__FILE__)}/fixtures"
62
+ target_file = "/tmp/Cities.js"
63
+ expected_target_file = "#{fixtures}/Cities.js"
64
+
65
+ FileUtils.rm(target_file) if File.exist?(target_file)
66
+
67
+ @compiler.compile("#{fixtures}/Cities.ejs", "or.ui", target_file)
68
+ File.open(target_file).read.should == File.open(expected_target_file).read
69
+ end
70
+ end
@@ -0,0 +1,6 @@
1
+ <ul>
2
+ <%# Loop through each city %>
3
+ <% $.each(cities, function() { %>
4
+ <li><%= this.name %></li>
5
+ <% }) %>
6
+ </ul>
@@ -0,0 +1,21 @@
1
+ // Autogenerated from an Ejs file. Edits may be lost.
2
+
3
+
4
+ window.or = window.or || {};
5
+ window.or.ui = window.or.ui || {};
6
+ window.or.ui.Cities = window.or.ui.Cities || {};
7
+ or.ui.Cities.template = function(options) {
8
+ var p = [];
9
+ with(options) {
10
+ p.push('<ul>\n ');
11
+ /* Loop through each city */
12
+ p.push('\n ');
13
+ $.each(cities, function() {
14
+ p.push('\n <li>');
15
+ p.push(this.name);
16
+ p.push('</li>\n ');
17
+ })
18
+ p.push('\n</ul>\n');
19
+ }
20
+ return p.join('');
21
+ }
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Parser" do
4
+ before(:each) do
5
+ @parser = Ejs::Parser.new
6
+ end
7
+
8
+ it "should parse static content" do
9
+ parsed = parse("<p>Static output</p>")
10
+ parsed.size.should == 1
11
+ parsed[0].node_type.should == 'static_content'
12
+ parsed[0].text_value.should == '<p>Static output</p>'
13
+ end
14
+
15
+ it "should parse scriplet" do
16
+ parsed = parse("<% console.log('test') %>")
17
+ parsed.size.should == 1
18
+ parsed[0].node_type.should == 'scriplet'
19
+ parsed[0].text_value.should == " console.log('test') "
20
+ end
21
+
22
+ it "should parse expression" do
23
+ parsed = parse("<%= this.name %>")
24
+ parsed.size.should == 1
25
+ parsed[0].node_type.should == 'expression'
26
+ parsed[0].text_value.should == " this.name "
27
+ end
28
+
29
+ it "should parse comment" do
30
+ parsed = parse("<%# This is a comment %>")
31
+ parsed.size.should == 1
32
+ parsed[0].node_type.should == 'comment'
33
+ parsed[0].text_value.should == " This is a comment "
34
+ end
35
+
36
+ it "should escape left and right delimter in script content" do
37
+ parsed = parse("<% console.log('Example: <%% puts 'test' %%>') %>")
38
+ parsed.size.should == 1
39
+ parsed[0].node_type.should == 'scriplet'
40
+ parsed[0].text_value.should == " console.log('Example: <% puts 'test' %>') "
41
+ end
42
+
43
+ it "should escape elft and right delimeter in staic content" do
44
+ parsed = parse("<%% this is a test %%>")
45
+ parsed.size.should == 1
46
+ parsed[0].node_type.should == 'static_content'
47
+ parsed[0].text_value.should == "<% this is a test %>"
48
+ end
49
+
50
+ private
51
+
52
+ def parse(ejs_conent)
53
+ result = @parser.parse(ejs_conent)
54
+ result.should_not be_nil
55
+ result.elements
56
+ end
57
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,25 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rubygems'
4
+ require 'ejs-rcompiler'
5
+ require 'spec'
6
+ require 'spec/autorun'
7
+
8
+ Spec::Runner.configure do |config|
9
+
10
+ end
11
+
12
+ Spec::Matchers.define :equal_ignoring_spaces do |expected|
13
+ match do |actual|
14
+ clean_spaces(actual) == clean_spaces(expected)
15
+ end
16
+
17
+ def clean_spaces(text)
18
+ text = "" if text.nil?
19
+ text.
20
+ gsub(/\s+/, " ").
21
+ strip.
22
+ gsub(/' /, "'").
23
+ gsub(/ '/, "'")
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ejs-rcompiler
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Ben Curren
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-27 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: treetop
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 4
30
+ - 0
31
+ version: 1.4.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 2
44
+ - 9
45
+ version: 1.2.9
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: Ejs to Javascript compiler.
49
+ email: ben@outright.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - LICENSE
56
+ - README.rdoc
57
+ files:
58
+ - .document
59
+ - .gitignore
60
+ - LICENSE
61
+ - README.rdoc
62
+ - Rakefile
63
+ - VERSION
64
+ - autotest/discover.rb
65
+ - ejs-rcompiler.gemspec
66
+ - lib/ejs-rcompiler.rb
67
+ - lib/ejs/compiler.rb
68
+ - lib/ejs/grammar.rb
69
+ - lib/ejs/grammar.treetop
70
+ - lib/ejs/parse_error.rb
71
+ - lib/ejs/parser.rb
72
+ - spec/compiler_spec.rb
73
+ - spec/fixtures/Cities.ejs
74
+ - spec/fixtures/Cities.js
75
+ - spec/parser_spec.rb
76
+ - spec/spec.opts
77
+ - spec/spec_helper.rb
78
+ has_rdoc: true
79
+ homepage: http://github.com/bcurren/ejs-rcompiler
80
+ licenses: []
81
+
82
+ post_install_message:
83
+ rdoc_options:
84
+ - --charset=UTF-8
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ segments:
99
+ - 0
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project:
104
+ rubygems_version: 1.3.6
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Ejs to Javascript compiler written in Ruby. This allows you to define HTML heavy Javascript components in EJS and compile them down to Javascript functions for later use.
108
+ test_files:
109
+ - spec/compiler_spec.rb
110
+ - spec/parser_spec.rb
111
+ - spec/spec_helper.rb