cerubis 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +14 -0
  3. data/Gemfile +15 -0
  4. data/README.md +111 -0
  5. data/Rakefile +15 -0
  6. data/TODO.md +31 -0
  7. data/cerubis.gemspec +22 -0
  8. data/examples/blocks.rb +20 -0
  9. data/examples/full/blocks/script_block.rb +8 -0
  10. data/examples/full/config.rb +14 -0
  11. data/examples/full/example.rb +25 -0
  12. data/examples/full/helpers/html_helpers.rb +13 -0
  13. data/examples/full/helpers/include_helper.rb +15 -0
  14. data/examples/full/models/page.rb +17 -0
  15. data/examples/full/models/site.rb +13 -0
  16. data/examples/full/templates/_footer.cerubis +1 -0
  17. data/examples/full/templates/_header.cerubis +1 -0
  18. data/examples/full/templates/main.cerubis +31 -0
  19. data/examples/helpers.rb +22 -0
  20. data/examples/html.rb +18 -0
  21. data/examples/variables.rb +10 -0
  22. data/lib/cerubis.rb +55 -0
  23. data/lib/cerubis/block.rb +27 -0
  24. data/lib/cerubis/block_node.rb +37 -0
  25. data/lib/cerubis/blocks/if.rb +8 -0
  26. data/lib/cerubis/blocks/loop.rb +20 -0
  27. data/lib/cerubis/blocks/unless.rb +9 -0
  28. data/lib/cerubis/condition.rb +59 -0
  29. data/lib/cerubis/context.rb +30 -0
  30. data/lib/cerubis/helper.rb +12 -0
  31. data/lib/cerubis/matcher.rb +19 -0
  32. data/lib/cerubis/method.rb +19 -0
  33. data/lib/cerubis/node.rb +27 -0
  34. data/lib/cerubis/objects/array.rb +4 -0
  35. data/lib/cerubis/objects/fixnum.rb +4 -0
  36. data/lib/cerubis/objects/float.rb +4 -0
  37. data/lib/cerubis/objects/hash.rb +4 -0
  38. data/lib/cerubis/objects/string.rb +4 -0
  39. data/lib/cerubis/parser.rb +125 -0
  40. data/lib/cerubis/syntax_error.rb +4 -0
  41. data/lib/cerubis/template.rb +21 -0
  42. data/lib/cerubis/text_node.rb +10 -0
  43. data/lib/cerubis/variable_replacement.rb +34 -0
  44. data/lib/cerubis/version.rb +3 -0
  45. data/test/all.rb +3 -0
  46. data/test/cerubis/block_node_test.rb +36 -0
  47. data/test/cerubis/blocks/if_test.rb +24 -0
  48. data/test/cerubis/blocks/loop_test.rb +25 -0
  49. data/test/cerubis/blocks/unless_test.rb +25 -0
  50. data/test/cerubis/condition_test.rb +142 -0
  51. data/test/cerubis/context_test.rb +33 -0
  52. data/test/cerubis/helper_test.rb +17 -0
  53. data/test/cerubis/matcher_test.rb +20 -0
  54. data/test/cerubis/method_test.rb +60 -0
  55. data/test/cerubis/parser_test.rb +48 -0
  56. data/test/cerubis/template_test.rb +38 -0
  57. data/test/cerubis/text_node_test.rb +16 -0
  58. data/test/cerubis_test.rb +31 -0
  59. data/test/matchers/test_block_name.rb +25 -0
  60. data/test/matchers/test_close_block.rb +25 -0
  61. data/test/matchers/test_conditions.rb +21 -0
  62. data/test/matchers/test_helpers.rb +21 -0
  63. data/test/matchers/test_object_method.rb +37 -0
  64. data/test/matchers/test_open_block.rb +57 -0
  65. data/test/matchers/test_operators.rb +29 -0
  66. data/test/matchers/test_variable.rb +37 -0
  67. data/test/methods/test_array_methods.rb +21 -0
  68. data/test/methods/test_fixnum_methods.rb +6 -0
  69. data/test/methods/test_float_methods.rb +6 -0
  70. data/test/methods/test_hash_methods.rb +11 -0
  71. data/test/methods/test_string_methods.rb +11 -0
  72. data/test/nodes/test_node_defaults.rb +30 -0
  73. data/test/rendered_test.rb +159 -0
  74. data/test/test_helper.rb +34 -0
  75. metadata +149 -0
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp/*
6
+ docs/*
7
+ .rbx/*
8
+ coverage.data
9
+ coverage/*
10
+ docs/* # for now
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ bundler_args: --without development
2
+ rvm:
3
+ #- 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby
7
+ - rbx
8
+ - rbx-2.0
9
+ #- ree
10
+ - ruby-head
11
+ script: "ruby -Itest:lib test/all.rb"
12
+ branches:
13
+ only:
14
+ - master
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
@@ -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,8 @@
1
+ class ScriptBlock
2
+ include Cerubis::VariableReplacement
3
+ include Cerubis::Block
4
+
5
+ def render
6
+ %{<script>#{replace_variables(yield)}</script>}
7
+ end
8
+ end
@@ -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,13 @@
1
+ class Site
2
+ include Cerubis::Method
3
+
4
+ attr :title
5
+ attr :homepage
6
+
7
+ cerubis_method :title, :homepage
8
+
9
+ def initialize(title, homepage)
10
+ @title = title
11
+ @homepage = homepage
12
+ end
13
+ end
@@ -0,0 +1 @@
1
+ <footer>&copy;{{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>
@@ -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