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.
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