hmote 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6f391daf28188242490899c275ffeae71b3aab6
4
- data.tar.gz: bc569d4bb4e614bc7a6ddbeeee38e8cb5dbecf7d
3
+ metadata.gz: 2bd46a8c3c5b7fdccdbc4bc125330763de517667
4
+ data.tar.gz: a049d0e48e684f6471b1fec0a4f1354c40263e30
5
5
  SHA512:
6
- metadata.gz: f367bf5d0fe6fecd1a5276f4ea1a22d89f5dac7b9712f45a5097f38db2b6b5cb660e4d0e399c06d5f745782001ccf00bdaee4e23dcccc6f2da889c6a30085974
7
- data.tar.gz: 63b4f9a3d394726212b89d3fa0a0182db3c38c1e147b0b6afdc93df74eba11da454ff06d12d95a058382085341705b9495294efc84ef9c94f212cfe632cd2dba
6
+ metadata.gz: 99f43f5f506bdbbf8e9613c1e1bd8f5239ff90fae229c9976b7d08d9365109bc0f76d89aaa09e34e0d984420c6f040087f5c5520b06411cd007579affb2f9903
7
+ data.tar.gz: 262abfe709226712b17c044a1b5567b3a5d66b9a5c51773a4a9ece75e43fe745a94830ee27cf7e94766cb00fec1be5ccc20f79489c4831fda93806fafb0f70d0
data/.gems CHANGED
@@ -1,2 +1,4 @@
1
- cutest -v 1.2.1
1
+ cutest -v 1.2.2
2
2
  hache -v 1.1.0
3
+ cuba -v 3.3.0
4
+ rack-test -v 0.6.2
data/LICENSE CHANGED
@@ -1,5 +1,4 @@
1
- Copyright (c) 2014 Francesco Rodríguez
2
- Copyright (c) 2011-2014 Michel Martens
1
+ Copyright (c) 2014-Present Francesco Rodríguez, Mayn Kjær
3
2
 
4
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
4
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,29 +1,202 @@
1
1
  hmote
2
2
  =====
3
3
 
4
- Minimum Operational Template. Same as [mote][mote] but it escapes HTML
5
- tag characters by default. It uses [hache][hache] to do the escaping.
4
+ Minimal template engine with default escaping.
6
5
 
7
- Usage
8
- -----
6
+ Basic Usage
7
+ -----------
9
8
 
10
- HMote escapes tag characters by default:
9
+ This is a basic example:
11
10
 
12
11
  ```ruby
13
- {{ '<img src="img.jpeg">' }}
14
- # => &lt;img src=&quot;img.jpeg&quot;&gt;
12
+ require "hmote"
13
+
14
+ template = HMote.parse("your template goes here!")
15
+ template.call
16
+ # => "your template goes here!"
17
+ ```
18
+
19
+ HMote recognizes two tags to evaluate Ruby code: `%` and `{{}}`.
20
+ The difference between them is that while the `%` tag only evaluates
21
+ the code, the `{{}}` tag also prints the result to the template.
22
+
23
+ Imagine that your template looks like this:
24
+
25
+ ```ruby
26
+ % gems = ["rack", "cuba", "hmote"]
27
+
28
+ <ul>
29
+ % # this is a comment.
30
+ % gems.sort.each do |gem|
31
+ <li>{{ gem }}</li>
32
+ % end
33
+ </ul>
34
+ ```
35
+
36
+ The generated result will be:
37
+
38
+ ```html
39
+ <ul>
40
+ <li>cuba</li>
41
+ <li>hmote</li>
42
+ <li>rack</li>
43
+ </ul>
44
+ ```
45
+
46
+ Parameters
47
+ ----------
48
+
49
+ The values passed to the template are available as local variables:
50
+
51
+ ```ruby
52
+ template = HMote.parse("Hello {{ name }}", self, [:name])
53
+ template.call(name: "Ruby")
54
+ # => Hello Ruby
55
+ ```
56
+
57
+ You can also use the `params` local variable to access the given
58
+ parameters:
59
+
60
+ ```ruby
61
+ template = HMote.parse("Hello {{ params[:name] }}", self)
62
+ template.call(name: "Ruby")
63
+ # => Hello Ruby
64
+ ```
65
+
66
+ Auto-escaping
67
+ -------------
68
+
69
+ By default, HMote escapes HTML special characters to prevent [XSS][xss]
70
+ attacks. You can start the expression with an exclamation mark to disable
71
+ escaping for that expression:
72
+
73
+ ```ruby
74
+ template = HMote.parse("Hello {{ name }}", self, [:name])
75
+ template.call(name: "<b>World</b>")
76
+ # => Hello &lt;b&gt;World&lt;b&gt;
77
+
78
+ template = HMote.parse("Hello {{! name }}", self, [:name])
79
+ template.call(name: "<b>World</b>")
80
+ # => Hello <b>World</b>
81
+ ```
82
+
83
+ HMote::Helpers
84
+ --------------
85
+
86
+ There's a helper available in the `HMote::Helpers` module, and you are
87
+ free to include it in your code. To do it, just type:
88
+
89
+ ```ruby
90
+ include HMote::Helpers
91
+ ```
92
+
93
+ ### Using the hmote helper
94
+
95
+ The `hmote` helper receives a file name and a hash and returns the rendered
96
+ version of its content. The compiled template is cached for subsequent calls.
97
+
98
+ ```ruby
99
+ hmote("test/basic.mote", n: 3)
100
+ # => "***\n"
15
101
  ```
16
102
 
17
- If you want to print content without escaping, then add `>` as
18
- shown below:
103
+ ### Template caching
104
+
105
+ When the `hmote` helper is first called with a template name, the
106
+ file is read and parsed, and a proc is created and stored in the
107
+ current thread. The parameters passed are defined as local variables
108
+ in the template. If you want to provide more parameters once the template
109
+ was cached, you won't be able to access the values as local variables,
110
+ but you can always access the `params` hash.
111
+
112
+ For example:
19
113
 
20
114
  ```ruby
21
- {{> '<img src="img.jpeg">' }}
22
- # => <img src="img.jpeg">
115
+ # First call
116
+ hmote("foo.mote", a: 1, b: 2)
23
117
  ```
24
118
 
25
- For more information about the usage, check the
26
- [Mote project homepage](https://github.com/soveran/mote).
119
+ HMote::Render
120
+ -------------
121
+
122
+ To use HMote in [Cuba][cuba], you need to load the `HMote::Render`
123
+ plugin as shown below:
124
+
125
+ ```ruby
126
+ require "hmote"
127
+ require "hmote/render"
128
+
129
+ Cuba.plugin(HMote::Render)
130
+ ```
131
+
132
+ `HMote::Render` provides three helper methods for rendering templates:
133
+ `partial`, `view` and `render`.
134
+
135
+ ```ruby
136
+ Cuba.define do
137
+ on "about" do
138
+ # `partial` renders a template without a layout.
139
+ res.write partial("about")
140
+ end
141
+
142
+ on "home" do
143
+ # `view` renders a template within a layout.
144
+ res.write view("about")
145
+ end
146
+
147
+ on "contact" do
148
+ # `render` is a shortcut to `res.write view(...)`
149
+ render("contact")
150
+ end
151
+ end
152
+ ```
153
+
154
+ By default, `HMote::Render` assumes that all view templates are placed
155
+ in a folder named `views` and that they use the `.mote` extension. Also
156
+ for `view` and `render` methods, it assumes that the layout template is
157
+ called `layout.mote`.
158
+
159
+ The defaults can be changed through the `Cuba.settings` method:
160
+
161
+ ```ruby
162
+ Cuba.settings[:hmote][:views] = "./views/admin/"
163
+ Cuba.settings[:hmote][:layout] = "admin"
164
+ ```
165
+
166
+ ### Layouts
167
+
168
+ To render inner content into a layout, use the `{{! content }}` tag.
169
+
170
+ ```html
171
+ <html>
172
+ <head>
173
+ <title>Mote Layout</title>
174
+ </head>
175
+ <body>
176
+ <h1>Hello, world!</h1>
177
+
178
+ {{! content }}
179
+ </body>
180
+ </html>
181
+ ```
182
+
183
+ ### Helpers
184
+
185
+ You can use the `app` variable to access the application helpers.
186
+
187
+ ```ruby
188
+ Cuba.define do
189
+ def h(unsafe)
190
+ ...
191
+ end
192
+ end
193
+ ```
194
+
195
+ ```html
196
+ <h1>{{! app.h("unsafe") }}</h1>
197
+
198
+ {{ app.partial("list") }}
199
+ ```
27
200
 
28
201
  Installation
29
202
  ------------
@@ -32,5 +205,5 @@ Installation
32
205
  $ gem install hmote
33
206
  ```
34
207
 
35
- [mote]: https://github.com/soveran/mote
36
- [hache]: https://github.com/frodsan/hache
208
+ [cuba]: http://cuba.is
209
+ [xss]: http://en.wikipedia.org/wiki/Cross-Site_Scripting
data/benchmarks/.gems ADDED
@@ -0,0 +1,2 @@
1
+ actionview -v 4.1.8
2
+ benchmark-ips -v 2.1.0
@@ -0,0 +1,17 @@
1
+ require_relative "helper"
2
+ require_relative "hmote_helper"
3
+ require_relative "rails_helper"
4
+
5
+ text = %q(some < text > inside & these " escapable' characters/1234)
6
+
7
+ Benchmark.ips do |x|
8
+ x.report("hmote") { hmote(text: text) }
9
+ x.report("rails") { rails(text: text) }
10
+ end
11
+
12
+ # Calculating -------------------------------------
13
+ # hmote 13.122k i/100ms
14
+ # rails 7.907k i/100ms
15
+ # -------------------------------------------------
16
+ # hmote 148.020k (± 3.6%) i/s - 747.954k
17
+ # rails 88.623k (± 2.7%) i/s - 442.792k
@@ -0,0 +1,8 @@
1
+ require "benchmark/ips"
2
+
3
+ def template_path(template)
4
+ File.join(File.expand_path("templates", __dir__), template)
5
+ end
6
+
7
+ MOTE_TEMPLATE = template_path("mote")
8
+ ERB_TEMPLATE = template_path("erb")
@@ -0,0 +1,7 @@
1
+ require_relative "../lib/hmote"
2
+
3
+ include HMote::Helpers
4
+
5
+ def hmote(params)
6
+ super(MOTE_TEMPLATE, params)
7
+ end
@@ -0,0 +1,28 @@
1
+ require "action_view"
2
+
3
+ class Context
4
+ class LookupContext
5
+ def disable_cache
6
+ yield
7
+ end
8
+
9
+ def find_template(*args)
10
+ end
11
+ end
12
+
13
+ def lookup_context
14
+ @lookup_context ||= LookupContext.new
15
+ end
16
+ end
17
+
18
+ ACTIONVIEW_CONTEXT = Context.new
19
+ ACTIONVIEW_TEMPLATE = ActionView::Template.new(
20
+ File.read(ERB_TEMPLATE), "template",
21
+ ActionView::Template::Handlers::ERB.new,
22
+ format: :html, virtual_path: "template"
23
+ )
24
+ ACTIONVIEW_TEMPLATE.locals = [:text]
25
+
26
+ def rails(params)
27
+ ACTIONVIEW_TEMPLATE.render(ACTIONVIEW_CONTEXT, params)
28
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "helper"
2
+ require_relative "hmote_helper"
3
+ require_relative "rails_helper"
4
+
5
+ text = %q(some text without escapable characters).html_safe
6
+
7
+ Benchmark.ips do |x|
8
+ x.report("hmote") { hmote(text: text) }
9
+ x.report("rails") { rails(text: text) }
10
+ end
11
+
12
+ # Calculating -------------------------------------
13
+ # hmote 29.035k i/100ms
14
+ # rails 14.607k i/100ms
15
+ # -------------------------------------------------
16
+ # hmote 367.044k (± 5.9%) i/s - 1.829M
17
+ # rails 157.936k (± 5.9%) i/s - 788.778k
@@ -0,0 +1 @@
1
+ <%= text %>
@@ -0,0 +1 @@
1
+ {{ text }}
data/hmote.gemspec CHANGED
@@ -1,11 +1,11 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "hmote"
3
- s.version = "1.0.0"
3
+ s.version = "1.1.0"
4
4
  s.summary = "A minimum operational template that escapes HTML tags by default."
5
5
  s.description = s.summary
6
- s.authors = ["Francesco Rodríguez"]
7
- s.email = ["frodsan@me.com"]
8
- s.homepage = "http://github.com/frodsan/hmote"
6
+ s.authors = ["Francesco Rodríguez", "Mayn Kjær"]
7
+ s.email = ["frodsan@me.com", "mayn.kjaer@gmail.com"]
8
+ s.homepage = "https://github.com/harmoni-io/hmote"
9
9
  s.license = "MIT"
10
10
 
11
11
  s.files = `git ls-files`.split("\n")
@@ -0,0 +1,31 @@
1
+ require_relative "../hmote"
2
+
3
+ module HMote::Render
4
+ include HMote::Helpers
5
+
6
+ def self.setup(app)
7
+ app.settings[:hmote] ||= {}
8
+ app.settings[:hmote][:views] ||= File.expand_path("views", Dir.pwd)
9
+ app.settings[:hmote][:layout] ||= "layout"
10
+ end
11
+
12
+ def render(template, params = {}, layout = settings[:hmote][:layout])
13
+ res.write(view(template, params, layout))
14
+ end
15
+
16
+ def view(template, params = {}, layout = settings[:hmote][:layout])
17
+ return partial(layout, params.merge(content: partial(template, params)))
18
+ end
19
+
20
+ def partial(template, params = {})
21
+ return hmote(template_path(template), params.merge(app: self), TOPLEVEL_BINDING)
22
+ end
23
+
24
+ def template_path(template)
25
+ if template.end_with?(".mote")
26
+ return template
27
+ else
28
+ return File.join(settings[:hmote][:views], "#{template}.mote")
29
+ end
30
+ end
31
+ end
data/lib/hmote.rb CHANGED
@@ -1,11 +1,14 @@
1
1
  require "hache"
2
2
 
3
- class Mote
4
- PATTERN = /^(\n)|^\s*(%)\s*(.*?)(?:\n|\Z)|(<\?)\s+(.*?)\s+\?>|(\{\{>?)(.*?)\}\}/m
3
+ class HMote
4
+ PATTERN = /^(\n)| # new lines.
5
+ ^\s*(%)\s*(.*?)(?:\n|\Z)| # % code
6
+ (<\?)\s+(.*?)\s+\?>| # <? multi-line code ?>
7
+ (\{\{!?)(.*?)\}\} # {{ escaped }} or {{! unescaped }}
8
+ /mx
5
9
 
6
10
  def self.parse(template, context = self, vars = [])
7
11
  terms = template.split(PATTERN)
8
-
9
12
  parts = "Proc.new do |params, __o|\n params ||= {}; __o ||= ''\n"
10
13
 
11
14
  vars.each do |var|
@@ -17,7 +20,7 @@ class Mote
17
20
  when "<?" then parts << "#{terms.shift}\n"
18
21
  when "%" then parts << "#{terms.shift}\n"
19
22
  when "{{" then parts << "__o << Hache.h((#{terms.shift}).to_s)\n"
20
- when "{{>" then parts << "__o << (#{terms.shift}).to_s\n"
23
+ when "{{!" then parts << "__o << (#{terms.shift}).to_s\n"
21
24
  else parts << "__o << #{term.dump}\n"
22
25
  end
23
26
  end
@@ -32,13 +35,13 @@ class Mote
32
35
  end
33
36
 
34
37
  module Helpers
35
- def mote(file, params = {}, context = self)
36
- mote_cache[file] ||= Mote.parse(File.read(file), context, params.keys)
37
- mote_cache[file][params]
38
+ def hmote(file, params = {}, context = self)
39
+ hmote_cache[file] ||= HMote.parse(File.read(file), context, params.keys)
40
+ hmote_cache[file].call(params)
38
41
  end
39
42
 
40
- def mote_cache
41
- Thread.current[:_mote_cache] ||= {}
43
+ def hmote_cache
44
+ Thread.current[:_hmote_cache] ||= {}
42
45
  end
43
46
  end
44
47
  end
data/makefile CHANGED
@@ -1,4 +1,7 @@
1
1
  .PHONY: test
2
2
 
3
+ gem:
4
+ gem build hmote.gemspec
5
+
3
6
  test:
4
7
  cutest test/*.rb
@@ -0,0 +1 @@
1
+ <h1>Custom</h1>
@@ -0,0 +1,2 @@
1
+ <title>{{ title }}</title>
2
+ {{! content }}
data/test/helper.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "cutest"
2
+ require_relative "../lib/hmote"
data/test/helpers.rb CHANGED
@@ -1,29 +1,25 @@
1
- require_relative "../lib/hmote"
1
+ require_relative "helper"
2
2
 
3
3
  class Cutest::Scope
4
- include Mote::Helpers
4
+ include HMote::Helpers
5
5
 
6
6
  def foo
7
7
  "foo"
8
8
  end
9
9
  end
10
10
 
11
- scope do
11
+ scope("helpers") do
12
12
  prepare do
13
- mote_cache.clear
14
- end
15
-
16
- test "helpers" do
17
- assert_equal " *\n *\n *\n", mote("test/fixtures/basic.mote", n: 3)
13
+ hmote_cache.clear
18
14
  end
19
15
 
20
16
  test "using functions in the context" do
21
- assert_equal "foo\n", mote("test/fixtures/foo.mote")
17
+ assert_equal("foo\n", hmote("test/foo.mote"))
22
18
  end
23
19
 
24
20
  test "passing in a context" do
25
21
  assert_raise(NameError) do
26
- mote("test/fixtures/foo.mote", {}, TOPLEVEL_BINDING)
22
+ hmote("test/foo.mote", {}, TOPLEVEL_BINDING)
27
23
  end
28
24
  end
29
25
  end
data/test/parsing.rb ADDED
@@ -0,0 +1,108 @@
1
+ require_relative "helper"
2
+
3
+ scope("parsing") do
4
+ test "assignment" do
5
+ template = HMote.parse("{{ 1 + 2 }}")
6
+
7
+ assert_equal("3", template.call)
8
+ end
9
+
10
+ test "control flow" do
11
+ template = (<<-EOT).gsub(/ {4}/, "")
12
+ % if false
13
+ false
14
+ % else
15
+ true
16
+ % end
17
+ EOT
18
+
19
+ result = HMote.parse(template).call
20
+
21
+ assert_equal(" true\n", result)
22
+ end
23
+
24
+ test "parameters" do
25
+ template = (<<-EOT).gsub(/ {4}/, "")
26
+ % params[:n].times do
27
+ *
28
+ % end
29
+ EOT
30
+
31
+ example = HMote.parse(template)
32
+
33
+ assert_equal("*\n*\n*\n", example[n: 3])
34
+ assert_equal("*\n*\n*\n*\n", example[n: 4])
35
+ end
36
+
37
+ test "multiline" do
38
+ example = HMote.parse("The\nMan\nAnd\n{{\"The\"}}\nSea")
39
+ assert_equal("The\nMan\nAnd\nThe\nSea", example[n: 3])
40
+ end
41
+
42
+ test "quotes" do
43
+ example = HMote.parse("'foo' 'bar' 'baz'")
44
+ assert_equal("'foo' 'bar' 'baz'", example.call)
45
+ end
46
+
47
+ test "context" do
48
+ context = Object.new
49
+ def context.user; "Bruno"; end
50
+
51
+ example = HMote.parse("{{ user }}", context)
52
+
53
+ assert_equal("Bruno", example.call)
54
+ end
55
+
56
+ test "locals" do
57
+ example = HMote.parse("{{ user }}", TOPLEVEL_BINDING, [:user])
58
+
59
+ assert_equal("Mayn", example.call(user: "Mayn"))
60
+ end
61
+
62
+ test "nil" do
63
+ example = HMote.parse("{{ params[:user] }}", TOPLEVEL_BINDING)
64
+
65
+ assert_equal("", example.call(user: nil))
66
+ end
67
+
68
+ test "multi-line XML-style directives" do
69
+ template = (<<-EOT).gsub(/^ /, "")
70
+ <? res = ""
71
+ [1, 2, 3].each_with_index do |item, idx|
72
+ res << "%d. %d\n" % [idx + 1, item * item]
73
+ end
74
+ ?>
75
+ {{ res }}
76
+ EOT
77
+
78
+ example = HMote.parse(template)
79
+
80
+ assert_equal("\n1. 1\n2. 4\n3. 9\n\n", example.call)
81
+ end
82
+
83
+ test "preserve XML directives" do
84
+ template = (<<-EOT).gsub(/^ /, "")
85
+ <?xml "hello" ?>
86
+ EOT
87
+
88
+ example = HMote.parse(template)
89
+
90
+ assert_equal("<?xml \"hello\" ?>\n", example.call)
91
+ end
92
+
93
+ test "escapes by default" do
94
+ text = %q(<>&"'/)
95
+ template = HMote.parse("{{ params[:text] }}")
96
+ result = template.call(text: text)
97
+
98
+ assert_equal("&lt;&gt;&amp;&quot;&#x27;&#x2F;", result)
99
+ end
100
+
101
+ test "no escaping please" do
102
+ text = %q(<>&"'/)
103
+ template = HMote.parse("{{! params[:text] }}")
104
+ result = template.call(text: text)
105
+
106
+ assert_equal(text, result)
107
+ end
108
+ end
data/test/render.rb ADDED
@@ -0,0 +1,78 @@
1
+ require_relative "helper"
2
+ require "cuba/test"
3
+ require_relative "../lib/hmote/render"
4
+
5
+ Cuba.plugin(HMote::Render)
6
+ Cuba.settings[:hmote][:views] = "./test/views"
7
+
8
+ Cuba.define do
9
+ def name
10
+ "App"
11
+ end
12
+
13
+ on "partial" do
14
+ res.write partial("home")
15
+ end
16
+
17
+ on "view" do
18
+ res.write view("home", title: "Hello")
19
+ end
20
+
21
+ on "render" do
22
+ render("home", title: "Hola")
23
+ end
24
+
25
+ on "context" do
26
+ res.write partial("context")
27
+ end
28
+
29
+ on "absolute" do
30
+ render("./test/custom_views/custom.mote", title: "Custom")
31
+ end
32
+
33
+ on "absolute_layout" do
34
+ render("./test/custom_views/custom.mote", title: "Custom Layout")
35
+ end
36
+ end
37
+
38
+ scope do
39
+ test "view renders view with layout" do
40
+ expected = "<title>Hello</title>\n<h1>Home</h1>"
41
+
42
+ get "/view"
43
+
44
+ assert last_response.body[expected]
45
+ end
46
+
47
+ test "partial renders view without layout" do
48
+ get "/partial"
49
+
50
+ assert last_response.body["<h1>Home</h1>"]
51
+ end
52
+
53
+ test "render renders view with layout" do
54
+ get "/render"
55
+
56
+ assert last_response.body["<title>Hola</title>\n<h1>Home</h1>"]
57
+ end
58
+
59
+ test "access to application context" do
60
+ get "/context"
61
+
62
+ assert last_response.body["App"]
63
+ end
64
+
65
+ test "use of absolute path for template" do
66
+ get "/absolute"
67
+
68
+ assert last_response.body["<title>Custom</title>\n<h1>Custom</h1>"]
69
+ end
70
+
71
+ test "use of absolute path for layout" do
72
+ Cuba.settings[:hmote][:layout] = "./test/custom_views/layout.mote"
73
+
74
+ get "/absolute_layout"
75
+
76
+ assert last_response.body["<title>Custom Layout</title>\n<h1>Custom</h1>"]
77
+ end
78
+ end
@@ -0,0 +1 @@
1
+ {{ app.name }}
@@ -0,0 +1 @@
1
+ <h1>Home</h1>
@@ -0,0 +1,2 @@
1
+ <title>{{ title }}</title>
2
+ {{! content }}
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hmote
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Rodríguez
8
+ - Mayn Kjær
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-08-05 00:00:00.000000000 Z
12
+ date: 2014-12-14 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: hache
@@ -41,6 +42,7 @@ dependencies:
41
42
  description: A minimum operational template that escapes HTML tags by default.
42
43
  email:
43
44
  - frodsan@me.com
45
+ - mayn.kjaer@gmail.com
44
46
  executables: []
45
47
  extensions: []
46
48
  extra_rdoc_files: []
@@ -48,14 +50,29 @@ files:
48
50
  - ".gems"
49
51
  - LICENSE
50
52
  - README.md
53
+ - benchmarks/.gems
54
+ - benchmarks/escaping.rb
55
+ - benchmarks/helper.rb
56
+ - benchmarks/hmote_helper.rb
57
+ - benchmarks/rails_helper.rb
58
+ - benchmarks/safe.rb
59
+ - benchmarks/templates/erb
60
+ - benchmarks/templates/mote
51
61
  - hmote.gemspec
52
62
  - lib/hmote.rb
63
+ - lib/hmote/render.rb
53
64
  - makefile
54
- - test/fixtures/basic.mote
55
- - test/fixtures/foo.mote
65
+ - test/custom_views/custom.mote
66
+ - test/custom_views/layout.mote
67
+ - test/foo.mote
68
+ - test/helper.rb
56
69
  - test/helpers.rb
57
- - test/hmote.rb
58
- homepage: http://github.com/frodsan/hmote
70
+ - test/parsing.rb
71
+ - test/render.rb
72
+ - test/views/context.mote
73
+ - test/views/home.mote
74
+ - test/views/layout.mote
75
+ homepage: https://github.com/harmoni-io/hmote
59
76
  licenses:
60
77
  - MIT
61
78
  metadata: {}
@@ -1,3 +0,0 @@
1
- % 3.times do
2
- *
3
- % end
data/test/hmote.rb DELETED
@@ -1,151 +0,0 @@
1
- require_relative "../lib/hmote"
2
-
3
- scope do
4
- test "empty lines" do
5
- example = Mote.parse("\n\n \n")
6
- assert_equal "\n\n \n", example.call
7
- end
8
-
9
- test "empty lines with mixed code" do
10
- example = Mote.parse("\n% true\n\n% false\n\n")
11
- assert_equal "\n\n\n", example.call
12
- end
13
-
14
- test "empty lines with control flow" do
15
- example = Mote.parse("\n% if true\n\n\n% else\n\n% end\n")
16
- assert_equal "\n\n\n", example.call
17
- end
18
-
19
- test "control flow without final newline" do
20
- example = Mote.parse("\n% if true\n\n\n% else\n\n% end")
21
- assert_equal "\n\n\n", example.call
22
- end
23
-
24
- test "assignment" do
25
- example = Mote.parse("{{ \"***\" }}")
26
- assert_equal "***", example.call
27
- end
28
-
29
- test "comment" do
30
- template = (<<-EOT).gsub(/ {4}/, "")
31
- *
32
- % # "*"
33
- *
34
- EOT
35
-
36
- example = Mote.parse(template)
37
- assert_equal "*\n*\n", example.call.squeeze("\n")
38
- end
39
-
40
- test "control flow" do
41
- template = (<<-EOT).gsub(/ {4}/, "")
42
- % if false
43
- *
44
- % else
45
- ***
46
- % end
47
- EOT
48
-
49
- example = Mote.parse(template)
50
- assert_equal " ***\n", example.call
51
- end
52
-
53
- test "block evaluation" do
54
- template = (<<-EOT).gsub(/ {4}/, "")
55
- % 3.times {
56
- *
57
- % }
58
- EOT
59
-
60
- example = Mote.parse(template)
61
- assert_equal "*\n*\n*\n", example.call
62
- end
63
-
64
- test "parameters" do
65
- template = (<<-EOT).gsub(/ {4}/, "")
66
- % params[:n].times {
67
- *
68
- % }
69
- EOT
70
-
71
- example = Mote.parse(template)
72
- assert_equal "*\n*\n*\n", example[:n => 3]
73
- assert_equal "*\n*\n*\n*\n", example[:n => 4]
74
- end
75
-
76
- test "multiline" do
77
- example = Mote.parse("The\nMan\nAnd\n{{\"The\"}}\nSea")
78
- assert_equal "The\nMan\nAnd\nThe\nSea", example[:n => 3]
79
- end
80
-
81
- test "quotes" do
82
- example = Mote.parse("'foo' 'bar' 'baz'")
83
- assert_equal "'foo' 'bar' 'baz'", example.call
84
- end
85
-
86
- test "context" do
87
- context = Object.new
88
- def context.user; "Bruno"; end
89
-
90
- example = Mote.parse("{{ context.user }}", context, [:context])
91
- assert_equal "Bruno", example.call(context: context)
92
- end
93
-
94
- test "locals" do
95
- example = Mote.parse("{{ user }}", TOPLEVEL_BINDING, [:user])
96
- assert_equal "Bruno", example.call(user: "Bruno")
97
- end
98
-
99
- test "nil" do
100
- example = Mote.parse("{{ params[:user] }}", TOPLEVEL_BINDING, [:user])
101
- assert_equal "", example.call(user: nil)
102
- end
103
-
104
- test "curly bug" do
105
- example = Mote.parse("{{ [1, 2, 3].map { |i| i * i }.join(',') }}")
106
- assert_equal "1,4,9", example.call
107
- end
108
-
109
- test "multi-line XML-style directives" do
110
- template = (<<-EOT).gsub(/^ /, "")
111
- <? res = ""
112
- [1, 2, 3].each_with_index do |item, idx|
113
- res << "%d. %d\n" % [idx + 1, item * item]
114
- end
115
- ?>
116
- {{ res }}
117
- EOT
118
-
119
- example = Mote.parse(template)
120
- assert_equal "\n1. 1\n2. 4\n3. 9\n\n", example.call
121
- end
122
-
123
- test "preserve XML directives" do
124
- template = (<<-EOT).gsub(/^ /, "")
125
- <?xml "hello" ?>
126
- EOT
127
-
128
- example = Mote.parse(template)
129
- assert_equal "<?xml \"hello\" ?>\n", example.call
130
- end
131
-
132
- test "escapes html" do
133
- template = (<<-EOT).gsub(/^ /, "")
134
- % res = '<img src="img.jpeg">'
135
- {{ res }}
136
- EOT
137
-
138
- example = Mote.parse(template)
139
- assert_equal "&lt;img src=&quot;img.jpeg&quot;&gt;\n", example.call
140
- end
141
-
142
- test "outputs raw html" do
143
- template = (<<-EOT).gsub(/^ /, "")
144
- % res = '<img src="img.jpeg">'
145
- {{> res }}
146
- EOT
147
-
148
- example = Mote.parse(template)
149
- assert_equal "<img src=\"img.jpeg\">\n", example.call
150
- end
151
- end
File without changes