cerubis 0.0.1
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.
- 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 [][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
|