├── .gems ├── makefile ├── test ├── example.ate └── test_ate.rb ├── ate.gemspec ├── LICENSE ├── lib └── ate.rb └── README.md /.gems: -------------------------------------------------------------------------------- 1 | cutest -v 1.1.3 -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | 3 | test: 4 | cutest test/*.rb -------------------------------------------------------------------------------- /test/example.ate: -------------------------------------------------------------------------------- 1 | % 3.times do 2 | Beetlejuice 3 | % end -------------------------------------------------------------------------------- /ate.gemspec: -------------------------------------------------------------------------------- 1 | require "./lib/ate" 2 | 3 | Gem::Specification.new do |s| 4 | s.name = "ate" 5 | s.version = Ate::VERSION 6 | s.summary = "Atractive Template Engine for minimalist people." 7 | s.description = "Ate is a minimalist and fast template engine." 8 | s.authors = ["Julio Lopez"] 9 | s.email = ["ljuliom@gmail.com"] 10 | s.homepage = "http://github.com/TheBlasfem/ate" 11 | s.files = Dir[ 12 | "LICENSE", 13 | "README.md", 14 | "lib/**/*.rb", 15 | "*.gemspec", 16 | "test/**/*.rb" 17 | ] 18 | s.license = "MIT" 19 | s.add_development_dependency "cutest", "1.1.3" 20 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Julio Lopez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /lib/ate.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Julio Lopez 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | class Ate 21 | VERSION = "1.0.0" 22 | 23 | class << self 24 | def parse(template, vars = {}) 25 | context = vars.fetch(:context, self) 26 | lines = template.end_with?(".ate") ? File.read(template) : template 27 | lines = lines.split("\n") 28 | built = build_proc(lines, vars) 29 | @parsed = context.instance_eval(built) 30 | self 31 | end 32 | 33 | def build_proc(lines, vars) 34 | @output = "Proc.new do \n output = \"\" \n " 35 | declaring_local_variables(vars) 36 | parsing_lines(lines) 37 | @output << "output \n end" 38 | end 39 | 40 | def declaring_local_variables(vars) 41 | vars.each do |x, y| 42 | value = y.is_a?(String) ? "\"#{y}\"" : y 43 | @output << "#{x} = #{value}\n" 44 | end 45 | end 46 | 47 | def parsing_lines(lines) 48 | lines.each do |line| 49 | if line =~ /^\s*(%)(.*?)$/ 50 | @output << "#{line.gsub(/^\s*%(.*?)$/, '\1') } \n" 51 | else 52 | @output << "output << %Q|#{line.gsub(/\{\{([^\r\n]*)\}\}/, '#{\1}')}\n| \n " 53 | end 54 | end 55 | end 56 | 57 | def render 58 | @parsed.call 59 | end 60 | end 61 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ate 2 | ==== 3 | 4 | Inspired by [mote][1], Ate is a minimalist framework-agnostic template engine. 5 | 6 | ## Introduction 7 | 8 | Template engines are things of all days. And this days most of them have several hundred (erb) if not thousand (erubis) of lines of code, even slim isn't so slim. So, Ate was written with the requirements of be simple and easy to use. 9 | 10 | ## Installation 11 | 12 | Installing Ate is as simple as running: 13 | 14 | ``` 15 | $ gem install ate 16 | ``` 17 | 18 | Include Ate in your Gemfile with gem 'ate' or require it with require 'ate'. 19 | 20 | Usage 21 | ----- 22 | 23 | Is very similar than other template engines for Ruby as Liquid, Mote, etc: 24 | 25 | ```ruby 26 | template = Ate.parse("Hello World") 27 | template.render #=> "Hello World" 28 | ``` 29 | 30 | ## Ruby code 31 | 32 | Lines that start with `%` are evaluated as Ruby code. 33 | 34 | ``` 35 | % if true 36 | Hi 37 | % else 38 | No, I won't display me 39 | % end 40 | ``` 41 | 42 | As this is ruby code, you can comment as you has always done. 43 | 44 | ``` 45 | % # I'm a comment. 46 | ``` 47 | 48 | And you can still doing any ruby thing: blocks, loops, etc. 49 | 50 | ``` 51 | % 3.times do |i| 52 | {{i}} 53 | % end 54 | ``` 55 | 56 | ## Variables 57 | 58 | To print a variable just use `{{` and `}}` 59 | 60 | Send a variables as a hash in the parse method to the template so it can get them: 61 | 62 | ```ruby 63 | template = Ate.parse("Hello, this is {{user}}", user: "dog") 64 | template.render #=> "Hello, this is dog" 65 | ``` 66 | 67 | Also, you can send other kinds of variables: 68 | 69 | ```ruby 70 | template = <<-EOT 71 | % items.each do |item| 72 | {{ item }} 73 | % end 74 | EOT 75 | parsed = Ate.parse(template, items: ["a", "b", "c"]) 76 | parsed.render #=> "a\nb\n\c" 77 | ``` 78 | 79 | You can even take advantage of do whatever operation inside the `{{ }}` 80 | 81 | ```ruby 82 | template = Ate.parse("The new price is: {{ price + 10 }}", price: 30) 83 | template.render #=> "The new price is: 40" 84 | ``` 85 | 86 | ## Contexts 87 | 88 | For send a particular context to your template, use the key `context` and your methods and variables will be called inside of your sent context. 89 | 90 | ```ruby 91 | class User 92 | def name 93 | "Julio" 94 | end 95 | end 96 | 97 | template = Ate.parse("Hi, I'm {{ name }}", context: User.new) 98 | template.render #=> "Hi, I'm Julio" 99 | ``` 100 | 101 | ## Templates 102 | 103 | In order to use Ate in a file template, use the suffix `.ate`, e.g. `public/index.html.ate` and add the path of your view in the parse method. Feel free to use any markup language as HTML. 104 | 105 | ```html 106 | 107 | 108 |

{{ main_title }}

109 | % posts.each do |post| 110 |
...
111 | % end 112 | 113 | ``` 114 | 115 | ```ruby 116 | template = Ate.parse("public/index.html.ate", main_title: "h1 title!", posts: array_of_posts) 117 | ``` 118 | 119 | [1]: https://github.com/soveran/mote -------------------------------------------------------------------------------- /test/test_ate.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../lib/ate", File.dirname(__FILE__)) 2 | 3 | scope do 4 | test "returning same number of empty lines" do 5 | parsed = Ate.parse("\n \n\n \n") 6 | assert_equal "\n \n\n \n", parsed.render 7 | end 8 | test "returning same number of empty lines with code" do 9 | parsed = Ate.parse("\n% false\n% true\n\n%true") 10 | assert_equal "\n\n", parsed.render 11 | end 12 | 13 | test "empty lines with conditional" do 14 | parsed = Ate.parse("\n% if true\n\n\n% else\n\n% end\n") 15 | assert_equal "\n\n\n", parsed.render 16 | end 17 | 18 | test "sending nil variable" do 19 | parsed = Ate.parse("{{ name }}", name: nil) 20 | assert_equal "\n", parsed.render 21 | end 22 | 23 | test "printing string" do 24 | parsed = Ate.parse('{{ "Hello World!" }}') 25 | assert_equal "Hello World!\n", parsed.render 26 | end 27 | 28 | test "running ruby code in display" do 29 | parsed = Ate.parse("{{ 2*2 }}") 30 | assert_equal "4\n", parsed.render 31 | end 32 | 33 | test "running an enumerator inside of {{}}" do 34 | example = Ate.parse("{{ [1, 2, 3].map { |i| i * i }.join(',') }}") 35 | assert_equal "1,4,9\n", example.render 36 | end 37 | 38 | test "comment" do 39 | template = (<<-EOT).gsub(/ /, "") 40 | Awesome 41 | % # i'm a comment 42 | ATE 43 | EOT 44 | 45 | parsed = Ate.parse(template) 46 | assert_equal "Awesome\nATE\n", parsed.render 47 | end 48 | 49 | test "respecting first empty spaces of lines" do 50 | template = " Hi, Juan\n Bye, I don't have time for this." 51 | parsed = Ate.parse(template) 52 | assert_equal " Hi, Juan\n Bye, I don't have time for this.\n", parsed.render 53 | end 54 | 55 | test "conditional operation" do 56 | template = (<<-EOT).gsub(/ {4}/, "") 57 | % if true 58 | I'll display to you 59 | % else 60 | I won't display, sorry 61 | % end 62 | EOT 63 | 64 | parsed = Ate.parse(template) 65 | assert_equal " I'll display to you\n", parsed.render 66 | end 67 | 68 | test "running a block" do 69 | template = (<<-EOT).gsub(/ {4}/, "") 70 | % 3.times do 71 | Beetlejuice 72 | % end 73 | EOT 74 | 75 | parsed = Ate.parse(template) 76 | assert_equal " Beetlejuice\n Beetlejuice\n Beetlejuice\n", parsed.render 77 | end 78 | 79 | test "int variables" do 80 | template = (<<-EOT).gsub(/ {4}/, "") 81 | % number.times { 82 | Pika {{type}} 83 | % } 84 | EOT 85 | 86 | parsed = Ate.parse(template, number: 3, type: 1000) 87 | number = 3 88 | assert_equal "Pika 1000\nPika 1000\nPika 1000\n", parsed.render 89 | end 90 | 91 | test "string variables" do 92 | parsed = Ate.parse("Hello {{name}}", name: "Julio") 93 | assert_equal "Hello Julio\n", parsed.render 94 | end 95 | 96 | test "mixing int and str variables" do 97 | template = (<<-EOT).gsub(/ {4}/, "") 98 | % n.times { 99 | {{ pokemon_name }} 100 | % } 101 | EOT 102 | 103 | parsed = Ate.parse(template, n: 3, pokemon_name: "Pikachu") 104 | assert_equal "Pikachu\nPikachu\nPikachu\n", parsed.render 105 | end 106 | 107 | test "recorring an array" do 108 | parsed = Ate.parse("% items.each do |item|\n{{item}}\n% end", items: ["a", "b", "c"]) 109 | assert_equal "a\nb\nc\n", parsed.render 110 | end 111 | 112 | test "case with multiple lines" do 113 | parsed = Ate.parse("Better\nCall\nSaul\n!") 114 | assert_equal "Better\nCall\nSaul\n!\n", parsed.render 115 | end 116 | 117 | test "with quotes" do 118 | parsed = Ate.parse("'this' 'awesome' 'quote'") 119 | assert_equal "'this' 'awesome' 'quote'\n", parsed.render 120 | end 121 | 122 | test "with double quotes" do 123 | parsed = Ate.parse("Hi, i'm testing \"double\"\"quotes\"") 124 | assert_equal "Hi, i'm testing \"double\"\"quotes\"\n", parsed.render 125 | end 126 | 127 | test "in a particular context" do 128 | class User 129 | attr_accessor :name 130 | end 131 | user = User.new 132 | user.name = "Julio" 133 | parsed = Ate.parse("{{ context.name }}", context: user) 134 | assert_equal "Julio\n", parsed.render 135 | end 136 | 137 | test "with html tags" do 138 | template = (<<-EOT).gsub(/ {4}/, "") 139 | 140 | 141 | {{ title }} 142 | 143 | 144 | EOT 145 | 146 | parsed = Ate.parse(template, title: "Cool Site!") 147 | assert_equal "\n \nCool Site!\n \n\n", parsed.render 148 | end 149 | 150 | test "soporting XML" do 151 | template = (<<-EOT).gsub(/ {4}/, "") 152 | 153 | EOT 154 | 155 | parsed = Ate.parse(template) 156 | assert_equal "\n", parsed.render 157 | end 158 | 159 | test "loading a file" do 160 | assert_equal " Beetlejuice\n Beetlejuice\n Beetlejuice\n", Ate.parse("test/example.ate").render 161 | end 162 | end --------------------------------------------------------------------------------