herb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5c394bc2c2281a98fffdbd20598cf1b8fb14c710
4
+ data.tar.gz: a3179a63d031f7599274f144ef7d846518a0bd96
5
+ SHA512:
6
+ metadata.gz: 02bd3e967c8d5433d141d4c4fb6c84c6610c2ca280413016def1280a7b4b77520289e582c070d030455f3a66b714cab34f292c46c7de94199e95ab8356d90b40
7
+ data.tar.gz: d2c7bfbd62d6188b5bbf7741b90113ffef0945ae7724f2e92ae98efc2d099a75496a3b4215836dc09bdd3a688776f547e46962a8457605eb219025bd34511c90
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 Francesco Rodriguez
2
+ Copyright (c) 2011-2016 Michel Martens
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
@@ -0,0 +1,171 @@
1
+ herb [![Build Status](https://travis-ci.org/frodsan/herb.svg)](https://travis-ci.org/frodsan/herb)
2
+ ====
3
+
4
+ Minimal template engine with default escaping.
5
+
6
+ Description
7
+ -----------
8
+
9
+ Herb is a fork of [Mote] that uses a [ERB]-like style syntax and auto-escape HTML special characters by default.
10
+
11
+ Installation
12
+ ------------
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem "herb"
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```
23
+ $ bundle
24
+ ```
25
+
26
+ Or install it yourself as:
27
+
28
+ ```
29
+ $ gem install herb
30
+ ```
31
+
32
+ Basic Usage
33
+ -----------
34
+
35
+ This is a basic example:
36
+
37
+ ```ruby
38
+ require "herb"
39
+
40
+ template = Herb.parse("your template goes here!")
41
+ template.call
42
+ # => "your template goes here!"
43
+ ```
44
+
45
+ Herb recognizes two tags to evaluate Ruby code: `<% %>`, and `<%= %>`. The difference between them is that while the `<% %>` tags only evaluate the code, the `<%= %>` tags also prints the result to the template.
46
+
47
+ Imagine that your template looks like this:
48
+
49
+ ```erb
50
+ <% # single line code %>
51
+ <% gems = ["rack", "cuba", "herb"] %>
52
+
53
+ <%
54
+ # multi-line code
55
+ sorted = gems.sort
56
+ %>
57
+
58
+ <ul>
59
+ <% sorted.each do |name| %>
60
+ <li><%= name %></li>
61
+ <% end %>
62
+ </ul>
63
+ ```
64
+
65
+ The generated result will be like:
66
+
67
+ ```html
68
+ <ul>
69
+ <li>cuba</li>
70
+ <li>herb</li>
71
+ <li>rack</li>
72
+ </ul>
73
+ ```
74
+
75
+ Parameters
76
+ ----------
77
+
78
+ The values passed to the template are available as local variables:
79
+
80
+ ```ruby
81
+ template = Herb.parse("Hello {{ name }}", [:name])
82
+ template.call(name: "Ruby")
83
+ # => Hello Ruby
84
+ ```
85
+
86
+ You can also use the `params` local variable to access the given parameters:
87
+
88
+ ```ruby
89
+ template = Herb.parse("Hello {{ params[:name] }}")
90
+ template.call(name: "Ruby")
91
+ # => Hello Ruby
92
+ ```
93
+
94
+ Auto-escaping
95
+ -------------
96
+
97
+ By default, Herb escapes HTML special characters to prevent [XSS][xss] attacks. You can start the expression with an exclamation mark to disable escaping for that expression:
98
+
99
+ ```ruby
100
+ template = Herb.parse("Hello {{ name }}", [:name])
101
+ template.call(name: "<b>World</b>")
102
+ # => Hello &lt;b&gt;World&lt;b&gt;
103
+
104
+ template = Herb.parse("Hello {{! name }}", [:name])
105
+ template.call(name: "<b>World</b>")
106
+ # => Hello <b>World</b>
107
+ ```
108
+
109
+ Herb::Helpers
110
+ --------------
111
+
112
+ There's a helper available in the `Herb::Helpers` module, and you are
113
+ free to include it in your code. To do it, just type:
114
+
115
+ ```ruby
116
+ include Herb::Helpers
117
+ ```
118
+
119
+ ### Using the `herb` helper
120
+
121
+ The `herb` helper receives a file name and a hash and returns the rendered version of its content. The compiled template is cached for subsequent calls.
122
+
123
+ ```ruby
124
+ herb("test/basic.erb", n: 3)
125
+ # => "***\n"
126
+ ```
127
+
128
+ ### Template caching
129
+
130
+ When the `herb` helper is first called with a template name, the file is read and parsed, and a proc is created and stored in the current thread. The parameters passed are defined as local variables in the template. If you want to provide more parameters once the template was cached, you won't be able to access the values as local variables, but you can always access the `params` hash.
131
+
132
+ For example:
133
+
134
+ ```ruby
135
+ # First call
136
+ herb("foo.erb", a: 1, b: 2)
137
+ ```
138
+
139
+ Contributing
140
+ ------------
141
+
142
+ Fork the project with:
143
+
144
+ ```
145
+ $ git clone git@github.com:frodsan/herb.git
146
+ ```
147
+
148
+ To install dependencies, use:
149
+
150
+ ```
151
+ $ bundle install
152
+ ```
153
+
154
+ To run the test suite, do:
155
+
156
+ ```
157
+ $ rake test
158
+ ```
159
+
160
+ For bug reports and pull requests use [GitHub][issues].
161
+
162
+ License
163
+ -------
164
+
165
+ Herb is released under the [MIT License][mit].
166
+
167
+ [erb]: http://ruby-doc.org/stdlib-2.3.1/libdoc/erb/rdoc/ERB.html
168
+ [issues]: https://github.com/frodsan/herb/issues
169
+ [mit]: http://www.opensource.org/licenses/MIT
170
+ [mote]: https://github.com/soveran/mote
171
+ [xss]: http://en.wikipedia.org/wiki/Cross-Site_Scripting
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2016 Francesco Rodríguez
4
+ # Copyright (c) 2011-2016 Michel Martens (https://github.com/soveran/mote)
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ module Herb
25
+ PATTERN = /(<%)\s+(.*?)\s+%>(?:\n)|(<%==?)(.*?)%>/m
26
+
27
+ def self.parse(template, vars = [], context = self)
28
+ terms = template.split(PATTERN)
29
+ parts = "proc do |params = {}, __o = ''|\n".dup
30
+
31
+ vars.each { |var| parts << sprintf("%s = params[%p]\n", var, var) }
32
+
33
+ while (term = terms.shift)
34
+ parts << parse_expression(terms, term)
35
+ end
36
+
37
+ parts << "__o; end"
38
+
39
+ compile(context, parts)
40
+ end
41
+
42
+ def self.parse_expression(terms, term)
43
+ case term
44
+ when "<%" then terms.shift << "\n"
45
+ when "<%=" then "__o << Herb.h((" + terms.shift << ").to_s)\n"
46
+ when "<%==" then "__o << (" + terms.shift << ").to_s\n"
47
+ else "__o << " + term.dump << "\n"
48
+ end
49
+ end
50
+
51
+ def self.compile(context, parts)
52
+ context.instance_eval(parts)
53
+ end
54
+
55
+ HTML_ESCAPE = {
56
+ "&" => "&amp;",
57
+ ">" => "&gt;",
58
+ "<" => "&lt;",
59
+ '"' => "&#39;",
60
+ "'" => "&#34;"
61
+ }.freeze
62
+
63
+ UNSAFE = /[&"'><]/
64
+
65
+ def self.h(str)
66
+ str.gsub(UNSAFE, HTML_ESCAPE)
67
+ end
68
+
69
+ module Helpers
70
+ def herb(file, params = {}, context = self)
71
+ herb_cache[file] ||= Herb.parse(File.read(file), params.keys, context)
72
+ herb_cache[file].call(params)
73
+ end
74
+
75
+ def herb_cache
76
+ Thread.current[:herb_cache] ||= {}
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "minitest/autorun"
5
+ require "minitest/pride"
6
+ require "minitest/sugar"
7
+ require_relative "../lib/herb"
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helper"
4
+
5
+ class HelpersTest < Minitest::Test
6
+ include Herb::Helpers
7
+
8
+ setup do
9
+ herb_cache.clear
10
+ end
11
+
12
+ def foo
13
+ "foo"
14
+ end
15
+
16
+ test "using functions in the context" do
17
+ assert_equal("foo\n", herb("test/views/foo.erb"))
18
+ end
19
+
20
+ test "passing in a context" do
21
+ assert_raises(NameError) do
22
+ herb("test/views/foo.erb", {}, TOPLEVEL_BINDING)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helper"
4
+
5
+ class ParsingTest < Minitest::Test
6
+ test "assignment" do
7
+ template = Herb.parse("<%= 1 + 2 %>")
8
+
9
+ assert_equal("3", template.call)
10
+ end
11
+
12
+ test "control flow" do
13
+ template = <<-EOT.gsub(/ {4}/, "")
14
+ <% if false %>
15
+ false
16
+ <% else %>
17
+ true
18
+ <% end %>
19
+ EOT
20
+
21
+ result = Herb.parse(template).call
22
+
23
+ assert_equal(" true\n", result)
24
+ end
25
+
26
+ test "parameters" do
27
+ template = <<-EOT.gsub(/ {4}/, "")
28
+ <% params[:n].times do %>
29
+ *
30
+ <% end %>
31
+ EOT
32
+
33
+ example = Herb.parse(template)
34
+
35
+ assert_equal("*\n*\n*\n", example[n: 3])
36
+ assert_equal("*\n*\n*\n*\n", example[n: 4])
37
+ end
38
+
39
+ test "multiline" do
40
+ example = Herb.parse("The\nMan\nAnd\n<%=\"The\"%>\nSea")
41
+
42
+ assert_equal("The\nMan\nAnd\nThe\nSea", example.call)
43
+ end
44
+
45
+ test "quotes" do
46
+ example = Herb.parse("'foo' 'bar' 'baz'")
47
+
48
+ assert_equal("'foo' 'bar' 'baz'", example.call)
49
+ end
50
+
51
+ test "context" do
52
+ context = Object.new
53
+
54
+ def context.user
55
+ "Bruno"
56
+ end
57
+
58
+ example = Herb.parse("<%= user %>", [], context)
59
+
60
+ assert_equal("Bruno", example.call)
61
+ end
62
+
63
+ test "locals" do
64
+ example = Herb.parse("<%= user %>", [:user], self)
65
+
66
+ assert_equal("Mayn", example.call(user: "Mayn"))
67
+ end
68
+
69
+ test "nil" do
70
+ example = Herb.parse("<%= params[:user] %>", [], TOPLEVEL_BINDING)
71
+
72
+ assert_equal("", example.call(user: nil))
73
+ end
74
+
75
+ test "multi-line" do
76
+ template = <<-EOT.gsub(/^ /, "")
77
+ <%
78
+ # Multiline code evaluation
79
+ lucky = [1, 3, 7, 9, 13, 15]
80
+ prime = [2, 3, 5, 7, 11, 13]
81
+ %>
82
+
83
+ <%= lucky & prime %>
84
+ EOT
85
+
86
+ example = Herb.parse(template)
87
+
88
+ assert_equal "\n[3, 7, 13]\n", example.call
89
+ end
90
+
91
+ test "escapes by default" do
92
+ text = %q(<>&"')
93
+
94
+ template = Herb.parse("<%= params[:text] %>")
95
+
96
+ result = template.call(text: text)
97
+
98
+ assert_equal("&lt;&gt;&amp;&#39;&#34;", result)
99
+ end
100
+
101
+ test "no escaping please" do
102
+ text = %q(<>&"')
103
+
104
+ template = Herb.parse("<%== params[:text] %>")
105
+
106
+ result = template.call(text: text)
107
+
108
+ assert_equal(text, result)
109
+ end
110
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: herb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Francesco Rodriguez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest-sugar
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '11.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '11.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.39'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.39'
69
+ description: ERB-like template engine that escapes HTML by default
70
+ email: frodsan@protonmail.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - LICENSE
76
+ - README.md
77
+ - lib/herb.rb
78
+ - test/helper.rb
79
+ - test/helpers_test.rb
80
+ - test/parsing_test.rb
81
+ homepage: https://github.com/frodsan/herb
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.5.1
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: ERB-like template engine that escapes HTML by default
105
+ test_files:
106
+ - test/helper.rb
107
+ - test/helpers_test.rb
108
+ - test/parsing_test.rb