ruby-ejs 1.0.0

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.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +58 -0
  4. data/lib/ejs.rb +275 -0
  5. data/lib/ruby/ejs.rb +1 -0
  6. metadata +118 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b4cdfc1984dfe0f7075d095ff8db803283877f7a81c3ab63f80f1d0148293bc1
4
+ data.tar.gz: 588d1484185dc5a8f0106fed729b4e2c0a740f44313edeccd9206aded072675e
5
+ SHA512:
6
+ metadata.gz: 937490d4c14a05dff25f1862defa7f62726887cac90d98776a17f205c9b347724003bf1a2be09b66276de2d6f577409f83be3d53e68dab7193db741a214ecc6c
7
+ data.tar.gz: 41834e5dddde678ebfd4073658e8e90807362e1b6588b140b74dc54349013265afa687fe3d9f861de08d6bb61487a4f1f139cf59505f30b6265e14af06ed0e6e
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Sam Stephenson
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.
@@ -0,0 +1,58 @@
1
+ EJS (Embedded JavaScript) template compiler for Ruby [![Circle CI](https://circleci.com/gh/malomalo/ruby-ejs.svg?style=svg)](https://circleci.com/gh/malomalo/ruby-ejs)
2
+ ====================================================
3
+
4
+ EJS templates embed JavaScript code inside `<% ... %>` tags, much like
5
+ ERB. This library is a port of
6
+ [Underscore.js](http://documentcloud.github.com/underscore/)'s
7
+ [`_.template`
8
+ function](http://documentcloud.github.com/underscore/#template) to
9
+ Ruby, and strives to maintain the same syntax and semantics.
10
+
11
+ Pass an EJS template to `EJS.compile` to generate a JavaScript
12
+ function:
13
+
14
+ EJS.compile("Hello <%= name %>")
15
+ # => "function(obj){...}"
16
+
17
+ Invoke the function in a JavaScript environment to produce a string
18
+ value. You can pass an optional object specifying local variables for
19
+ template evaluation.
20
+
21
+ The EJS tag syntax is as follows:
22
+
23
+ * `<% ... %>` silently evaluates the statement inside the tags.
24
+ * `<%= ... %>` evaluates the expression inside the tags and inserts
25
+ its string value into the template output.
26
+ * `<%- ... %>` behaves like `<%= ... %>` but HTML-escapes its output.
27
+
28
+ If a evalation tag (`<%=`) ends with an open function, the function
29
+ return a compiled template. For example:
30
+
31
+ ```erb
32
+ <% formTag = function(template) { return '<form>\n'+template()+'\n</form>'; } %>
33
+
34
+ <%= formTag(function () { %>
35
+ <input type="submit" />
36
+ <% }) %>
37
+ ```
38
+
39
+ generates:
40
+
41
+ ```html
42
+ <form>
43
+ <input type="submit" />
44
+ </form>
45
+ ```
46
+
47
+ If you have the [ExecJS](https://github.com/sstephenson/execjs/)
48
+ library and a suitable JavaScript runtime installed, you can pass a
49
+ template and an optional hash of local variables to `EJS.evaluate`:
50
+
51
+ EJS.evaluate("Hello <%= name %>", :name => "world")
52
+ # => "Hello world"
53
+
54
+ -----
55
+
56
+ &copy; 2012 Sam Stephenson
57
+
58
+ Released under the MIT license
@@ -0,0 +1,275 @@
1
+ module EJS
2
+
3
+ DEFAULTS = {
4
+ open_tag: '<%',
5
+ close_tag: '%>',
6
+
7
+ open_tag_modifiers: {
8
+ escape: '=',
9
+ unescape: '-',
10
+ comment: '#',
11
+ literal: '%'
12
+ },
13
+
14
+ close_tag_modifiers: {
15
+ trim: '-',
16
+ literal: '%'
17
+ },
18
+
19
+ escape: nil
20
+ }
21
+
22
+ ASSET_DIR = File.join(__dir__, 'ruby', 'ejs', 'assets')
23
+
24
+ class << self
25
+
26
+ # Compiles an EJS template to a JavaScript function. The compiled
27
+ # function takes an optional argument, an object specifying local
28
+ # variables in the template. You can optionally pass the
29
+ # `:evaluation_pattern` and `:interpolation_pattern` options to
30
+ # `compile` if you want to specify a different tag syntax for the
31
+ # template.
32
+ #
33
+ # EJS.compile("Hello <%= name %>")
34
+ # # => "function(obj){...}"
35
+ #
36
+ def transform(source, options = {})
37
+ options = default(options)
38
+
39
+ output = if options[:escape]
40
+ "import {" + options[:escape].split('.').reverse.join(" as escape} from '") + "';\n"
41
+ else
42
+ "import {escape} from 'ejs';\n"
43
+ end
44
+
45
+ output << "export default function (locals) {\n"
46
+ output << function_source(source, options)
47
+ output << "}"
48
+
49
+ output
50
+ end
51
+
52
+ def compile(source, options = {})
53
+ options = default(options)
54
+
55
+ output = "function(locals, escape) {\n"
56
+ output << function_source(source, options)
57
+ output << "}"
58
+ output
59
+ end
60
+
61
+
62
+ # Evaluates an EJS template with the given local variables and
63
+ # compiler options. You will need the ExecJS
64
+ # (https://github.com/sstephenson/execjs/) library and a
65
+ # JavaScript runtime available.
66
+ #
67
+ # EJS.evaluate("Hello <%= name %>", name: "world")
68
+ # # => "Hello world"
69
+ #
70
+ def evaluate(template, locals = {}, options = {})
71
+ require "execjs"
72
+ context = ExecJS.compile(<<-JS)
73
+ #{escape_function}
74
+
75
+ var template = #{compile(template, options)}
76
+ var evaluate = function(locals) {
77
+ return template(locals, escape);
78
+ }
79
+ JS
80
+ context.call("evaluate", locals)
81
+ end
82
+
83
+ protected
84
+
85
+ def default(options)
86
+ options = DEFAULTS.merge(options)
87
+
88
+ [:open_tag_modifiers, :close_tag_modifiers].each do |k|
89
+ DEFAULTS[k].each do |sk, v|
90
+ next if options[k].has_key?(sk)
91
+ options[k] = v
92
+ end
93
+ end
94
+
95
+ options
96
+ end
97
+
98
+ def escape_module
99
+ escape_function.sub('function', 'export function')
100
+ end
101
+
102
+ def escape_function(name='escape')
103
+ <<-JS
104
+ function #{name}(string) {
105
+ if (string !== undefined && string != null) {
106
+ return String(string).replace(/[&<>'"\\/]/g, function (c) {
107
+ return '&#' + c.codePointAt(0) + ';';
108
+ });
109
+ } else {
110
+ return '';
111
+ }
112
+ }
113
+ JS
114
+ end
115
+
116
+ def chars_balanced?(str, chars)
117
+ a = chars[0]
118
+ b = chars[1]
119
+ str = str.sub(/"(\\.|[^"])+"/, '')
120
+ str = str.sub(/'(\\.|[^'])+'/, '')
121
+ a_count = str.scan(/#{a}/).length
122
+ b_count = str.scan(/#{b}/).length
123
+
124
+ a_count - b_count
125
+ end
126
+
127
+
128
+ def digest(source, options)
129
+ open_tag_count = 0
130
+ close_tag_count = 0
131
+ tag_length = nil
132
+ # var index, tagType, tagModifier, tagModifiers, matchingModifier, prefix;
133
+ index = nil
134
+ tag_type = nil
135
+ tag_modifiers = nil
136
+ tag_modifier = nil
137
+ prefix =nil
138
+ matching_modifier = nil
139
+ last_tag_modifier = nil
140
+ next_open_index = source.index(options[:open_tag])
141
+ next_close_index = source.index(options[:close_tag])
142
+
143
+ while next_open_index || next_close_index
144
+ if (next_close_index && (!next_open_index || next_close_index < next_open_index))
145
+ index = next_close_index
146
+ tag_type = :close
147
+ tag_length = options[:close_tag].length
148
+ tag_modifiers = options[:close_tag_modifiers]
149
+ close_tag_count += 1
150
+ matching_modifier = tag_modifiers.find do |k, v|
151
+ source[index - v.length, v.length] == v
152
+ end
153
+ else
154
+ index = next_open_index
155
+ tag_type = :open
156
+ tag_length = options[:open_tag].length
157
+ tag_modifiers = options[:open_tag_modifiers]
158
+ open_tag_count += 1
159
+ matching_modifier = tag_modifiers.find do |k, v|
160
+ source[index + tag_length, v.length] == v
161
+ end
162
+ end
163
+
164
+ if matching_modifier
165
+ tag_length += matching_modifier[1].length
166
+ tag_modifier = matching_modifier[0]
167
+ else
168
+ tag_modifier = :default
169
+ end
170
+
171
+ if tag_modifier == :literal
172
+ if tag_type == :open
173
+ source = source[0, tag_length - matching_modifier[1].length] + source[(index + tag_length)..-1]
174
+ # source = source.slice(0, index + tagLength - matchingModifier[1].length) + source.slice(index + tagLength);
175
+ open_tag_count -= 1
176
+ else
177
+ close_tag_count -= 1
178
+ if index == 0
179
+ source = source[(index + matching_modifier[1].length)..-1]
180
+ else
181
+ source = source[0..index] + source[(index + matching_modifier[1].length)..-1]
182
+ end
183
+ end
184
+
185
+ next_open_index = source.index(options.openTag, index + tag_length - matching_modifier[1].length);
186
+ next_close_index = source.index(options.closeTag, index + tag_length - matching_modifier[1].length);
187
+ next
188
+ end
189
+
190
+ if index != 0
191
+ if tag_type == :close
192
+ if matching_modifier
193
+ yield(source[0...(matching_modifier[1].length)], :js, last_tag_modifier)
194
+ else
195
+ yield(source[0...index], :js, last_tag_modifier)
196
+ end
197
+ else
198
+ yield(source[0...index], :text, last_tag_modifier)
199
+ end
200
+
201
+ source = source[index..-1]
202
+ end
203
+
204
+ if tag_type == :close && matching_modifier
205
+ source = source[(tag_length - matching_modifier[1].length)..-1]
206
+ source.lstrip!
207
+ else
208
+ source = source[tag_length..-1]
209
+ end
210
+ next_open_index = source.index(options[:open_tag])
211
+ next_close_index = source.index(options[:close_tag])
212
+ last_tag_modifier = tag_modifier
213
+ end
214
+
215
+ if open_tag_count != close_tag_count
216
+ raise "Could not find closing tag for \"#{options[(tag_type.to_s + '_tag').to_sym]}\"."
217
+ end
218
+
219
+ yield(source, :text, tag_modifier)
220
+ end
221
+
222
+
223
+ def function_source(source, options)
224
+ stack = []
225
+ output = " var __output = [], __append = __output.push.bind(__output);\n"
226
+ output << " with (locals || {}) {\n"
227
+
228
+ digest(source, options) do |segment, type, modifier|
229
+ if type == :js
230
+ if segment.match(/\A\s*\}/m)
231
+ case stack.pop
232
+ when :escape
233
+ output << "\n return __output.join(\"\");\n"
234
+ output << segment << " ));\n"
235
+ when :unescape
236
+ output << "\n return __output.join(\"\");\n"
237
+ output << segment << " );\n"
238
+ else
239
+ output << " " << segment << "\n"
240
+ end
241
+ elsif segment.match(/\)\s*\{\s*\Z/m)
242
+ stack << modifier
243
+ case modifier
244
+ when :escape
245
+ output << " __append(escape(" << segment
246
+ output << "\n var __output = [], __append = __output.push.bind(__output);\n"
247
+ when :unescape
248
+ output << " __append(" << segment
249
+ output << "\n var __output = [], __append = __output.push.bind(__output);\n"
250
+ else
251
+ output << " " << segment << "\n"
252
+ end
253
+ else
254
+ case modifier
255
+ when :escape
256
+ output << " __append(escape(" << segment << "));\n"
257
+ when :unescape
258
+ output << " __append(" << segment << ");\n"
259
+ else
260
+ output << " " << segment << "\n"
261
+ end
262
+ end
263
+ elsif segment.length > 0
264
+ output << " __append(`" + segment.gsub("\\"){"\\\\"}.gsub(/\n/, '\\n').gsub(/\r/, '\\r') + "`);\n"
265
+ end
266
+ end
267
+
268
+ output << " }\n"
269
+ output << " return __output.join(\"\");\n"
270
+
271
+ output
272
+ end
273
+
274
+ end
275
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(__dir__, '..', 'ejs'))
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-ejs
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Bracy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: execjs
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Compile EJS (Embedded JavaScript) templates in Ruby.
84
+ email:
85
+ - jonbracy@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE
91
+ - README.md
92
+ - lib/ejs.rb
93
+ - lib/ruby/ejs.rb
94
+ homepage: https://github.com/malomalo/ruby-ejs
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.7.4
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: EJS (Embedded JavaScript) template compiler
118
+ test_files: []