cerubis 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/.travis.yml +14 -0
- data/Gemfile +15 -0
- data/README.md +111 -0
- data/Rakefile +15 -0
- data/TODO.md +31 -0
- data/cerubis.gemspec +22 -0
- data/examples/blocks.rb +20 -0
- data/examples/full/blocks/script_block.rb +8 -0
- data/examples/full/config.rb +14 -0
- data/examples/full/example.rb +25 -0
- data/examples/full/helpers/html_helpers.rb +13 -0
- data/examples/full/helpers/include_helper.rb +15 -0
- data/examples/full/models/page.rb +17 -0
- data/examples/full/models/site.rb +13 -0
- data/examples/full/templates/_footer.cerubis +1 -0
- data/examples/full/templates/_header.cerubis +1 -0
- data/examples/full/templates/main.cerubis +31 -0
- data/examples/helpers.rb +22 -0
- data/examples/html.rb +18 -0
- data/examples/variables.rb +10 -0
- data/lib/cerubis.rb +55 -0
- data/lib/cerubis/block.rb +27 -0
- data/lib/cerubis/block_node.rb +37 -0
- data/lib/cerubis/blocks/if.rb +8 -0
- data/lib/cerubis/blocks/loop.rb +20 -0
- data/lib/cerubis/blocks/unless.rb +9 -0
- data/lib/cerubis/condition.rb +59 -0
- data/lib/cerubis/context.rb +30 -0
- data/lib/cerubis/helper.rb +12 -0
- data/lib/cerubis/matcher.rb +19 -0
- data/lib/cerubis/method.rb +19 -0
- data/lib/cerubis/node.rb +27 -0
- data/lib/cerubis/objects/array.rb +4 -0
- data/lib/cerubis/objects/fixnum.rb +4 -0
- data/lib/cerubis/objects/float.rb +4 -0
- data/lib/cerubis/objects/hash.rb +4 -0
- data/lib/cerubis/objects/string.rb +4 -0
- data/lib/cerubis/parser.rb +125 -0
- data/lib/cerubis/syntax_error.rb +4 -0
- data/lib/cerubis/template.rb +21 -0
- data/lib/cerubis/text_node.rb +10 -0
- data/lib/cerubis/variable_replacement.rb +34 -0
- data/lib/cerubis/version.rb +3 -0
- data/test/all.rb +3 -0
- data/test/cerubis/block_node_test.rb +36 -0
- data/test/cerubis/blocks/if_test.rb +24 -0
- data/test/cerubis/blocks/loop_test.rb +25 -0
- data/test/cerubis/blocks/unless_test.rb +25 -0
- data/test/cerubis/condition_test.rb +142 -0
- data/test/cerubis/context_test.rb +33 -0
- data/test/cerubis/helper_test.rb +17 -0
- data/test/cerubis/matcher_test.rb +20 -0
- data/test/cerubis/method_test.rb +60 -0
- data/test/cerubis/parser_test.rb +48 -0
- data/test/cerubis/template_test.rb +38 -0
- data/test/cerubis/text_node_test.rb +16 -0
- data/test/cerubis_test.rb +31 -0
- data/test/matchers/test_block_name.rb +25 -0
- data/test/matchers/test_close_block.rb +25 -0
- data/test/matchers/test_conditions.rb +21 -0
- data/test/matchers/test_helpers.rb +21 -0
- data/test/matchers/test_object_method.rb +37 -0
- data/test/matchers/test_open_block.rb +57 -0
- data/test/matchers/test_operators.rb +29 -0
- data/test/matchers/test_variable.rb +37 -0
- data/test/methods/test_array_methods.rb +21 -0
- data/test/methods/test_fixnum_methods.rb +6 -0
- data/test/methods/test_float_methods.rb +6 -0
- data/test/methods/test_hash_methods.rb +11 -0
- data/test/methods/test_string_methods.rb +11 -0
- data/test/nodes/test_node_defaults.rb +30 -0
- data/test/rendered_test.rb +159 -0
- data/test/test_helper.rb +34 -0
- metadata +149 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in cerubis.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem 'ruby-debug19', :platform => :ruby_19
|
8
|
+
gem 'cover_me', :platform => :mri_19
|
9
|
+
end
|
10
|
+
|
11
|
+
group :test do
|
12
|
+
gem 'rake'
|
13
|
+
gem 'capybara'
|
14
|
+
gem 'minitest', '>= 2.5'
|
15
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# Cerubis [![Test Status](https://secure.travis-ci.org/daneharrigan/cerubis.png)][3]
|
2
|
+
|
3
|
+
*WARNING:* Cerubis is still under initial development. Most features are
|
4
|
+
not functional yet.
|
5
|
+
|
6
|
+
Cerubis is inspired by [Liquid Markup][1] and [Mustache][2]. I like how
|
7
|
+
Mustache syntax looks, but it's logic-less. Liquid allows just enough
|
8
|
+
logic, but I don't like the syntax. And so Cerubis was born.
|
9
|
+
|
10
|
+
## Syntax
|
11
|
+
|
12
|
+
A block is started with `{{# ... }}` and closed with `{{/ ... }}`.
|
13
|
+
Output is defined as `{{ ... }}`.
|
14
|
+
|
15
|
+
<header>
|
16
|
+
<h1>{{ page.title }}</h1>
|
17
|
+
{{#unless navigation.empty?}}
|
18
|
+
<menu>
|
19
|
+
{{#loop nav in navigation}}
|
20
|
+
<li>{{ link_to page }}</li>
|
21
|
+
{{/loop}}
|
22
|
+
</menu>
|
23
|
+
{{/unless}}
|
24
|
+
</header>
|
25
|
+
|
26
|
+
## Standard Blocks
|
27
|
+
|
28
|
+
* if
|
29
|
+
* unless
|
30
|
+
* loop
|
31
|
+
|
32
|
+
## Objects in Template Context
|
33
|
+
|
34
|
+
Rendering a Cerubis template with objects in the context is easy:
|
35
|
+
|
36
|
+
template_str = <<-STR
|
37
|
+
{{#if items.empty?}}
|
38
|
+
<p>There are no items!</p>
|
39
|
+
{{/if}}
|
40
|
+
STR
|
41
|
+
|
42
|
+
context = { items: [1,2,3] }
|
43
|
+
|
44
|
+
Cerubis.render(template_str, context)
|
45
|
+
|
46
|
+
You can create your own objects and add them to template context, but
|
47
|
+
you need to make Cerubis aware of the methods it's allowed to call:
|
48
|
+
|
49
|
+
class Foo
|
50
|
+
include Cerubis::Method
|
51
|
+
cerubis_method :foo, :bar, :baz
|
52
|
+
|
53
|
+
def foo; "Foo Method"; end
|
54
|
+
def bar; "Bar Method"; end
|
55
|
+
def baz; "Baz Method"; end
|
56
|
+
end
|
57
|
+
|
58
|
+
template_str = "<some-template-text>"
|
59
|
+
context = { foo: Foo.new }
|
60
|
+
|
61
|
+
Cerubis.render(template_str, context)
|
62
|
+
|
63
|
+
Shown above, object methods are made available to Cerubis templates by
|
64
|
+
indicating them with `cerubis_method`. The `cerubis_method` can have
|
65
|
+
many method names passed to it and `cerubis_method` can be called
|
66
|
+
multiple times.
|
67
|
+
|
68
|
+
## Template Helpers
|
69
|
+
|
70
|
+
Template helpers are meant to add convienence methods to your code.
|
71
|
+
Below you see how simple it is to add a helper and to use it in your
|
72
|
+
template:
|
73
|
+
|
74
|
+
# Adding a new helper
|
75
|
+
module FooHelper
|
76
|
+
def foo_helper(name, value)
|
77
|
+
"My name is #{name} and my value is #{value}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Cerubis.register_helper FooHelper
|
82
|
+
|
83
|
+
# Using the helper
|
84
|
+
content = <<-STR
|
85
|
+
Hello {{ foo_helper 'John Doe', '12' }}
|
86
|
+
STR
|
87
|
+
|
88
|
+
template = Cerubis.render(content)
|
89
|
+
template.to_html
|
90
|
+
|
91
|
+
## Testing
|
92
|
+
|
93
|
+
You can run the tests in a few different ways. First, you've got your
|
94
|
+
standard ruby way:
|
95
|
+
|
96
|
+
ruby -Ilib:test test/cerubis_test.rb
|
97
|
+
ruby -Ilib:test test/all.rb
|
98
|
+
|
99
|
+
Or you can run them all with Rake:
|
100
|
+
|
101
|
+
rake test
|
102
|
+
|
103
|
+
## Code Coverage
|
104
|
+
|
105
|
+
The [cover_me][4] gem has been added to the project. If you'd like to see
|
106
|
+
the current coverage just run `thor test:coverage`.
|
107
|
+
|
108
|
+
[1]: http://github.com/shopify/liquid
|
109
|
+
[2]: http://github.com/defunkt/mustache
|
110
|
+
[3]: https://secure.travis-ci.org/daneharrigan/cerubis
|
111
|
+
[4]: https://github.com/markbates/cover_me
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
desc 'Runs all test files'
|
5
|
+
task :test do
|
6
|
+
system "ruby -Ilib:test test/all.rb"
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'Generate test coverage report'
|
10
|
+
task :'test:coverage' do
|
11
|
+
require 'cover_me'
|
12
|
+
|
13
|
+
Rake::Task.invoke :test
|
14
|
+
CoverMe.complete!
|
15
|
+
end
|
data/TODO.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Cerubis TODO
|
2
|
+
|
3
|
+
## Hash arguments
|
4
|
+
|
5
|
+
Helpers only support an argument list. They don't support hashes.
|
6
|
+
Blocks don't support hashes either. I think it'll be harder to add hash
|
7
|
+
support to blocks than to helpers, but I see value in adding it to both.
|
8
|
+
|
9
|
+
{{#form_for page, as: f}}
|
10
|
+
{{f.text_field 'first_name', value: 'John'}}
|
11
|
+
{{f.text_field 'last_name', value: 'Smith'}}
|
12
|
+
{{f.text_area 'comments', value: 'Comments...'}}
|
13
|
+
{{f.submit 'Send!', id: 'comment-button'}}
|
14
|
+
{{/form_for}}
|
15
|
+
|
16
|
+
The code above is an example of how I think I'd like the hash syntax to
|
17
|
+
work in `Cerubis`. Note that I the "as: f" key/value pair does not have
|
18
|
+
quotes around the "f." This is intentional, to indicate that the value
|
19
|
+
can be any item within the template context.
|
20
|
+
|
21
|
+
## Refactors
|
22
|
+
|
23
|
+
The `Cerubis::Parser#nested_block?` and `Cerubis::Parser#blocks_not_closed?`
|
24
|
+
methods are very similar. I think they can be consolidated.
|
25
|
+
|
26
|
+
Add backwards compatibility for 1.8.7/ree
|
27
|
+
|
28
|
+
## Test coverage
|
29
|
+
|
30
|
+
At the time of writing, `Cerubis` sits at 98.8% coverage and less than
|
31
|
+
400 LOC. I'd like to see the coverage brought to 100%.`
|
data/cerubis.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "cerubis/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "cerubis"
|
7
|
+
s.version = Cerubis::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Dane Harrigan"]
|
10
|
+
s.email = ["dane.harrigan@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{A simple, secure, non-evaluating template engine}
|
13
|
+
s.description = %q{A simple, secure, non-evaluating template engine}
|
14
|
+
|
15
|
+
s.rubyforge_project = "cerubis"
|
16
|
+
s.required_ruby_version = '>= 1.9.2'
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
end
|
data/examples/blocks.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'cerubis'
|
3
|
+
|
4
|
+
content = <<-STR
|
5
|
+
<html>
|
6
|
+
{{#if true}}
|
7
|
+
<header>
|
8
|
+
{{#unless false}}
|
9
|
+
<ul>
|
10
|
+
{{#loop item in collection}}
|
11
|
+
<li>{{item}}</li>
|
12
|
+
{{/loop}}
|
13
|
+
</ul>
|
14
|
+
{{/unless}}
|
15
|
+
</header>
|
16
|
+
{{/if}}
|
17
|
+
</html>
|
18
|
+
STR
|
19
|
+
|
20
|
+
puts Cerubis.render(content, collection: [1,2,3])
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'cerubis'
|
3
|
+
require 'helpers/html_helpers'
|
4
|
+
require 'helpers/include_helper'
|
5
|
+
require 'blocks/script_block'
|
6
|
+
require 'models/page'
|
7
|
+
require 'models/site'
|
8
|
+
|
9
|
+
# register blocks
|
10
|
+
Cerubis.register_block :script, ScriptBlock
|
11
|
+
|
12
|
+
# register helpers
|
13
|
+
Cerubis.register_helper :stylesheet_link_tag, :javascript_include_tag, :link_to, HTMLHelpers
|
14
|
+
Cerubis.register_helper :include, IncludeHelper
|
@@ -0,0 +1,25 @@
|
|
1
|
+
ExampleRoot = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$: << ExampleRoot
|
3
|
+
|
4
|
+
require 'config'
|
5
|
+
|
6
|
+
# create page objects
|
7
|
+
subpages = [
|
8
|
+
Page.new('Sub-page: 1'),
|
9
|
+
Page.new('Sub-page: 2'),
|
10
|
+
Page.new('Sub-page: 3'),
|
11
|
+
Page.new('Sub-page: 4'),
|
12
|
+
Page.new('Sub-page: 5')
|
13
|
+
]
|
14
|
+
|
15
|
+
homepage = Page.new('Example Page Title', subpages)
|
16
|
+
|
17
|
+
# create site object
|
18
|
+
site = Site.new('Example Site', homepage: homepage)
|
19
|
+
|
20
|
+
# get main template
|
21
|
+
content = File.read("#{ExampleRoot}/templates/main.cerubis")
|
22
|
+
|
23
|
+
# render template
|
24
|
+
template = Cerubis.render(content, page: homepage, site: site, current_year: Time.now.year)
|
25
|
+
puts template.to_html
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module HTMLHelpers
|
2
|
+
def stylesheet_link_tag(name)
|
3
|
+
%{<link href="/stylesheets/#{name}.css" rel="stylesheet">}
|
4
|
+
end
|
5
|
+
|
6
|
+
def javascript_include_tag(name)
|
7
|
+
%{<script src="/javascripts/#{name}.js"></script>}
|
8
|
+
end
|
9
|
+
|
10
|
+
def link_to(title, url)
|
11
|
+
%{<a href="#{url}">#{title}</a>}
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module IncludeHelper
|
2
|
+
def include(name)
|
3
|
+
full_path = "#{template_root}/_#{name}.cerubis"
|
4
|
+
if File.exists?(full_path)
|
5
|
+
template = File.read(full_path)
|
6
|
+
|
7
|
+
Cerubis.render(template, context)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def template_root
|
13
|
+
@template_root ||= File.expand_path(File.dirname(__FILE__) + '/../templates')
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Page
|
2
|
+
include Cerubis::Method
|
3
|
+
|
4
|
+
attr :title
|
5
|
+
attr :subpages
|
6
|
+
|
7
|
+
cerubis_method :title, :subpages, :permalink
|
8
|
+
|
9
|
+
def initialize(title, subpages=[])
|
10
|
+
@title = title
|
11
|
+
@subpages = subpages
|
12
|
+
end
|
13
|
+
|
14
|
+
def permalink
|
15
|
+
"/#{@title.downcase.gsub(/\s/,'-')}"
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<footer>©{{current_year}} {{site.title}}</footer>
|
@@ -0,0 +1 @@
|
|
1
|
+
<header>{{site.title}}</header>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
{{stylesheet_link_tag 'main'}}
|
5
|
+
{{javascript_include_tag 'main'}}
|
6
|
+
<title>{{page.title}} | {{site.title}}</title>
|
7
|
+
{{#script}}
|
8
|
+
var name = '{{page.title}}';
|
9
|
+
console.log('Page loaded: ' + name);
|
10
|
+
{{/script}}
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
{{include 'header'}}
|
14
|
+
<div id="container">
|
15
|
+
<div id="content">
|
16
|
+
<h1>{{page.title}}</h1>
|
17
|
+
{{#if site.homepage === page}}
|
18
|
+
<h2>Welcome to {{page.title}}!</h2>
|
19
|
+
{{/if}}
|
20
|
+
</div>
|
21
|
+
<aside>
|
22
|
+
<ul>
|
23
|
+
{{#loop item in page.subpages}}
|
24
|
+
<li>{{link_to item.title, item.permalink}}</li>
|
25
|
+
{{/loop}}
|
26
|
+
</ul>
|
27
|
+
</aside>
|
28
|
+
</div>
|
29
|
+
{{include 'footer'}}
|
30
|
+
</body>
|
31
|
+
</html>
|
data/examples/helpers.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'cerubis'
|
3
|
+
|
4
|
+
Page = Struct.new(:title, :permalink)
|
5
|
+
page = Page.new('Homepage', '/')
|
6
|
+
|
7
|
+
module LinkTo
|
8
|
+
def link_to(page_or_title, permalink='')
|
9
|
+
title = page_or_title.is_a?(String) ? page_or_title : page_or_title.title
|
10
|
+
permalink = page_or_title.permalink unless page_or_title.is_a?(String)
|
11
|
+
|
12
|
+
%{<a href="#{permalink}">#{title}</a>}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
content = <<-STR
|
17
|
+
1: {{ link_to page }}
|
18
|
+
2: {{ link_to 'About Us', '/about-us' }}
|
19
|
+
STR
|
20
|
+
|
21
|
+
Cerubis.register_helper :link_to, LinkTo
|
22
|
+
puts Cerubis.render(content, page: page)
|
data/examples/html.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'cerubis'
|
3
|
+
|
4
|
+
content = <<-STR
|
5
|
+
<html>
|
6
|
+
<body>
|
7
|
+
{{#if true}}
|
8
|
+
<h1>Header Content</h1>
|
9
|
+
{{#if true}}
|
10
|
+
<p>Branding Message</p>
|
11
|
+
{{/if}}
|
12
|
+
{{/if}}
|
13
|
+
</body>
|
14
|
+
</html>
|
15
|
+
STR
|
16
|
+
|
17
|
+
template = Cerubis.render(content)
|
18
|
+
puts template.to_html
|