neapolitan 0.2.0
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/HISTORY.rdoc +14 -0
- data/LICENSE +23 -0
- data/README.rdoc +46 -0
- data/bin/neapolitan +3 -0
- data/lib/neapolitan.rb +2 -0
- data/lib/neapolitan/command.rb +84 -0
- data/lib/neapolitan/config.rb +30 -0
- data/lib/neapolitan/document.rb +76 -0
- data/lib/neapolitan/factory.rb +209 -0
- data/lib/neapolitan/meta/data.rb +27 -0
- data/lib/neapolitan/meta/gemfile +13 -0
- data/lib/neapolitan/meta/profile +23 -0
- data/lib/neapolitan/part.rb +36 -0
- data/lib/neapolitan/template.rb +150 -0
- data/meta/data.rb +27 -0
- data/meta/gemfile +13 -0
- data/meta/profile +23 -0
- data/qed/fixtures/example.html +39 -0
- data/qed/fixtures/example.npt +33 -0
- data/qed/fixtures/example.yaml +5 -0
- data/qed/overview.rdoc +81 -0
- metadata +188 -0
data/HISTORY.rdoc
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 Thomas Sawyer
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
23
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
= Neapolitan
|
2
|
+
|
3
|
+
* home: http://rubyworks.github.com/neapolitan
|
4
|
+
* code: http://github.com/rubyworks/neapolitan
|
5
|
+
|
6
|
+
|
7
|
+
== DESCRIPTION
|
8
|
+
|
9
|
+
There are many markup and templating systems in the
|
10
|
+
world. Why be limited to just one? Neapolitan gives
|
11
|
+
you a whole box to pick from.
|
12
|
+
|
13
|
+
|
14
|
+
== FEATURES/ISSUES
|
15
|
+
|
16
|
+
* All the variety of a Whitman's Sampler.
|
17
|
+
* And all the ease of a Hershey's K.I.S.S.
|
18
|
+
* Website uses pretty colors ;)
|
19
|
+
|
20
|
+
|
21
|
+
== SYNOPSIS
|
22
|
+
|
23
|
+
For now please see the Neapolitan website and API documentation.
|
24
|
+
|
25
|
+
|
26
|
+
== HOW TO INSTALL
|
27
|
+
|
28
|
+
You know the routine ;)
|
29
|
+
|
30
|
+
$ sudo gem install neapolitan
|
31
|
+
|
32
|
+
If you're old fashion and want to install to a site
|
33
|
+
location, see Setup.rb (http://protuils.github.com/setup).
|
34
|
+
|
35
|
+
|
36
|
+
== COPYRIGHT/LICENSE
|
37
|
+
|
38
|
+
Neapolitan, Copyright (c) 2010 Thomas Sawyer
|
39
|
+
|
40
|
+
Neapolitan is distributed under the terms of the Apache License v2.0.
|
41
|
+
|
42
|
+
THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
43
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
44
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
45
|
+
PURPOSE.
|
46
|
+
|
data/bin/neapolitan
ADDED
data/lib/neapolitan.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'neapolitan'
|
2
|
+
|
3
|
+
module Neapolitan
|
4
|
+
|
5
|
+
# Command line interface.
|
6
|
+
|
7
|
+
class Command
|
8
|
+
|
9
|
+
def self.main(*argv)
|
10
|
+
new(*argv).call
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(*argv)
|
14
|
+
@output = nil
|
15
|
+
@noharm = false
|
16
|
+
@trace = false
|
17
|
+
@data_file = nil
|
18
|
+
|
19
|
+
parser.parse!(argv)
|
20
|
+
|
21
|
+
@files = argv
|
22
|
+
end
|
23
|
+
|
24
|
+
def parser
|
25
|
+
OptionParser.new do |opt|
|
26
|
+
opt.banner = "neapolitan [file1 file2 ...]"
|
27
|
+
|
28
|
+
opt.on("--output", "-o [PATH]", "save output to specified directory") do |path|
|
29
|
+
@output = path
|
30
|
+
end
|
31
|
+
|
32
|
+
opt.on("--source", "-s [FILE]", "source data file") do |file|
|
33
|
+
@data_file = file
|
34
|
+
end
|
35
|
+
|
36
|
+
opt.on("--trace", "show extra operational information") do
|
37
|
+
$TRACE = true
|
38
|
+
end
|
39
|
+
|
40
|
+
opt.on("--dryrun", "-n", "don't actually write to disk") do
|
41
|
+
$DRYRUN = true
|
42
|
+
end
|
43
|
+
|
44
|
+
opt.on("--debug", "run in debug mode") do
|
45
|
+
$DEBUG = true
|
46
|
+
$VERBOSE = true
|
47
|
+
end
|
48
|
+
|
49
|
+
opt.on_tail("--help", "display this help message") do
|
50
|
+
puts opt
|
51
|
+
exit
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
def call
|
58
|
+
begin
|
59
|
+
@files.each do |file|
|
60
|
+
doc = Document.new(file, data)
|
61
|
+
if @output
|
62
|
+
#doc.save
|
63
|
+
else
|
64
|
+
puts doc
|
65
|
+
end
|
66
|
+
end
|
67
|
+
rescue => e
|
68
|
+
$DEBUG ? raise(e) : puts(e.message)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
def data
|
74
|
+
if @data_file
|
75
|
+
YAML.load(File.new(@data_file))
|
76
|
+
else
|
77
|
+
{} #@source ||= YAML.load(STDIN.read)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Neapolitan
|
4
|
+
|
5
|
+
# Configuration
|
6
|
+
class Config
|
7
|
+
|
8
|
+
#
|
9
|
+
DEFAULTS = {
|
10
|
+
:stencil => 'rhtml',
|
11
|
+
#:format => 'html',
|
12
|
+
:pagelayout => 'page',
|
13
|
+
:postlayout => 'post',
|
14
|
+
:maxchars => 500,
|
15
|
+
}
|
16
|
+
|
17
|
+
attr :defaults
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
if File.exist?('.config/defaults')
|
21
|
+
custom_defaults = YAML.load(File.new('.config/defaults'))
|
22
|
+
else
|
23
|
+
custom_defaults = {}
|
24
|
+
end
|
25
|
+
@defaults = OpenStruct.new(DEFAULTS.merge(custom_defaults))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'neapolitan/template'
|
2
|
+
|
3
|
+
module Neapolitan
|
4
|
+
|
5
|
+
# = Neapolitan Document
|
6
|
+
#
|
7
|
+
# The Document class encapsulates a file which
|
8
|
+
# can be then be rendered via a Neapolitan::Template.
|
9
|
+
class Document
|
10
|
+
|
11
|
+
# File path.
|
12
|
+
attr :file
|
13
|
+
|
14
|
+
#
|
15
|
+
attr :template
|
16
|
+
|
17
|
+
# New Document object.
|
18
|
+
#
|
19
|
+
# file - path to neapolitan formatted file
|
20
|
+
# options - configuration passed on to the Template class
|
21
|
+
#
|
22
|
+
# Returns a new Document object.
|
23
|
+
def initialize(file, options={})
|
24
|
+
case file
|
25
|
+
when File
|
26
|
+
@file = file.name
|
27
|
+
@text = file.read
|
28
|
+
@file.close
|
29
|
+
when String
|
30
|
+
@file = file
|
31
|
+
@text = File.read(file)
|
32
|
+
end
|
33
|
+
|
34
|
+
@template = Template.new(@text, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
def inspect
|
39
|
+
"<#{self.class}: @file='#{file}'>"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Name of file less extname.
|
43
|
+
#def name
|
44
|
+
# @name ||= file.chomp(File.extname(file))
|
45
|
+
#end
|
46
|
+
|
47
|
+
#
|
48
|
+
def render(data={}, &block)
|
49
|
+
@template.render(data, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
# TODO: how to handle extension?
|
53
|
+
def save(*path_and_data, &block)
|
54
|
+
data = Hash===path_and_data.last ? path_and_data.pop : {}
|
55
|
+
path = path_and_data
|
56
|
+
|
57
|
+
rendering = render(data, &block)
|
58
|
+
extension = rendering.header['extension'] || '.html'
|
59
|
+
|
60
|
+
path = Dir.pwd unless path
|
61
|
+
if File.directory?(path)
|
62
|
+
file = File.join(path, file.chomp(File.extname(file)) + extension)
|
63
|
+
else
|
64
|
+
file = path
|
65
|
+
end
|
66
|
+
|
67
|
+
if $DRYRUN
|
68
|
+
$stderr << "[DRYRUN] write #{fname}"
|
69
|
+
else
|
70
|
+
File.open(fname, 'w'){ |f| f << rendering.to_s }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
#require 'tilt'
|
2
|
+
require 'malt'
|
3
|
+
|
4
|
+
module Neapolitan
|
5
|
+
|
6
|
+
# Controls rendering to a variety of back-end templating
|
7
|
+
# and markup systems.
|
8
|
+
#
|
9
|
+
module Factory
|
10
|
+
extend self
|
11
|
+
|
12
|
+
#
|
13
|
+
def render(format, text, source, &yld)
|
14
|
+
#table = {}
|
15
|
+
#scope = Object.new
|
16
|
+
|
17
|
+
case source
|
18
|
+
when Hash
|
19
|
+
db = source
|
20
|
+
when Binding
|
21
|
+
db = source
|
22
|
+
else # object scope
|
23
|
+
db = source
|
24
|
+
end
|
25
|
+
|
26
|
+
case format
|
27
|
+
when /^coderay/
|
28
|
+
coderay(text, format)
|
29
|
+
when /^syntax/
|
30
|
+
syntax(text, format)
|
31
|
+
else
|
32
|
+
doc = Malt.text(text, :format=>format.to_sym)
|
33
|
+
doc.render(db, &yld)
|
34
|
+
#if engine = Tilt[format]
|
35
|
+
# engine.new{text}.render(scope, table, &yld)
|
36
|
+
#else
|
37
|
+
# text
|
38
|
+
#end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
#def redcloth(input)
|
44
|
+
# RedCloth.new(input).to_html
|
45
|
+
#end
|
46
|
+
|
47
|
+
#def bluecloth(input)
|
48
|
+
# BlueCloth.new(input).to_html
|
49
|
+
#end
|
50
|
+
|
51
|
+
#def rdiscount(input)
|
52
|
+
# RDiscount.new(input).to_html
|
53
|
+
#end
|
54
|
+
|
55
|
+
def rdoc(input)
|
56
|
+
markup = RDoc::Markup::ToHtml.new
|
57
|
+
markup.convert(input)
|
58
|
+
end
|
59
|
+
|
60
|
+
#def haml(input)
|
61
|
+
# Haml::Engine.new(input).render
|
62
|
+
#end
|
63
|
+
|
64
|
+
def coderay(input, format)
|
65
|
+
require 'coderay'
|
66
|
+
format = format.split('.')[1] || :ruby #:plaintext
|
67
|
+
tokens = CodeRay.scan(input, format.to_sym) #:ruby
|
68
|
+
tokens.div()
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
def syntax(input, format)
|
73
|
+
require 'syntax/convertors/html'
|
74
|
+
format = format.split('.')[1] || 'ruby' #:plaintext
|
75
|
+
lines = true
|
76
|
+
conv = Syntax::Convertors::HTML.for_syntax(format)
|
77
|
+
conv.convert(input,lines)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Stencil Rendering
|
81
|
+
# -----------------
|
82
|
+
|
83
|
+
#
|
84
|
+
#def render_stencil(stencil, text, attributes)
|
85
|
+
# case stencil
|
86
|
+
# when 'rhtml'
|
87
|
+
# erb(text, attributes)
|
88
|
+
# when 'liquid'
|
89
|
+
# liquid(text, attributes)
|
90
|
+
# else
|
91
|
+
# text
|
92
|
+
# end
|
93
|
+
#end
|
94
|
+
|
95
|
+
#
|
96
|
+
#def erb(input, attributes)
|
97
|
+
# template = ERB.new(input)
|
98
|
+
# context = TemplateContext.new(attributes)
|
99
|
+
# result = template.result(context.__binding__)
|
100
|
+
# result
|
101
|
+
#end
|
102
|
+
|
103
|
+
#def liquid(input, attributes)
|
104
|
+
# template = Liquid::Template.parse(input)
|
105
|
+
# result = template.render(attributes, :filters => [TemplateFilters])
|
106
|
+
# result
|
107
|
+
#end
|
108
|
+
|
109
|
+
=begin
|
110
|
+
# Require Dependencies
|
111
|
+
# --------------------
|
112
|
+
|
113
|
+
# TODO: Load engines only if used.
|
114
|
+
|
115
|
+
begin ; require 'rubygems' ; rescue LoadError ; end
|
116
|
+
begin ; require 'erb' ; rescue LoadError ; end
|
117
|
+
begin ; require 'redcloth' ; rescue LoadError ; end
|
118
|
+
begin ; require 'bluecloth' ; rescue LoadError ; end
|
119
|
+
begin ; require 'rdiscount' ; rescue LoadError ; end
|
120
|
+
|
121
|
+
begin
|
122
|
+
require 'liquid'
|
123
|
+
#Liquid::Template.register_filter(TemplateFilters)
|
124
|
+
rescue LoadError
|
125
|
+
end
|
126
|
+
|
127
|
+
begin
|
128
|
+
require 'haml'
|
129
|
+
#Haml::Template.options[:format] = :html5
|
130
|
+
rescue LoadError
|
131
|
+
end
|
132
|
+
=end
|
133
|
+
|
134
|
+
begin
|
135
|
+
require 'rdoc/markup'
|
136
|
+
require 'rdoc/markup/to_html'
|
137
|
+
rescue LoadError
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
=begin
|
143
|
+
# = Clean Rendering Context
|
144
|
+
#
|
145
|
+
# The Factory Context is is used by ERB.
|
146
|
+
|
147
|
+
class Context
|
148
|
+
#include TemplateFilters
|
149
|
+
|
150
|
+
instance_methods(true).each{ |m| private m unless m =~ /^__/ }
|
151
|
+
|
152
|
+
def initialize(attributes={})
|
153
|
+
@attributes = attributes
|
154
|
+
end
|
155
|
+
|
156
|
+
def __binding__
|
157
|
+
binding
|
158
|
+
end
|
159
|
+
|
160
|
+
def to_h
|
161
|
+
@attributes
|
162
|
+
end
|
163
|
+
|
164
|
+
def method_missing(s, *a)
|
165
|
+
s = s.to_s
|
166
|
+
@attributes.key?(s) ? @attributes[s] : super
|
167
|
+
end
|
168
|
+
end
|
169
|
+
=end
|
170
|
+
|
171
|
+
#
|
172
|
+
#
|
173
|
+
#
|
174
|
+
|
175
|
+
#module TemplateFilters
|
176
|
+
|
177
|
+
# NOTE: HTML truncate did not work well.
|
178
|
+
|
179
|
+
# # HTML comment regular expression
|
180
|
+
# REM_RE = %r{<\!--(.*?)-->}
|
181
|
+
#
|
182
|
+
# # HTML tag regular expression
|
183
|
+
# TAG_RE = %r{</?\w+((\s+\w+(\s*=\s*(?:"(.|\n)*?"|'(.|\n)*?'|[^'">\s]+))?)+\s*|\s*)/?>} #'
|
184
|
+
#
|
185
|
+
# #
|
186
|
+
# def truncate_html(html, limit)
|
187
|
+
# return html unless limit
|
188
|
+
#
|
189
|
+
# mask = html.gsub(REM_RE){ |m| "\0" * m.size }
|
190
|
+
# mask = mask.gsub(TAG_RE){ |m| "\0" * m.size }
|
191
|
+
#
|
192
|
+
# i, x = 0, 0
|
193
|
+
#
|
194
|
+
# while i < mask.size && x < limit
|
195
|
+
# x += 1 if mask[i] != "\0"
|
196
|
+
# i += 1
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# while x > 0 && mask[x,1] == "\0"
|
200
|
+
# x -= 1
|
201
|
+
# end
|
202
|
+
#
|
203
|
+
# return html[0..x]
|
204
|
+
# end
|
205
|
+
|
206
|
+
#end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Object.__send__(:remove_const, :VERSION) if Object.const_defined?(:VERSION) # becuase Ruby 1.8~ gets in the way
|
2
|
+
|
3
|
+
module Neapolitan
|
4
|
+
|
5
|
+
DIRECTORY = File.dirname(__FILE__)
|
6
|
+
|
7
|
+
def self.gemfile
|
8
|
+
@gemfile ||= (
|
9
|
+
require 'yaml'
|
10
|
+
YAML.load(File.new(DIRECTORY + '/gemfile'))
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.profile
|
15
|
+
@profile ||= (
|
16
|
+
require 'yaml'
|
17
|
+
YAML.load(File.new(DIRECTORY + '/profile'))
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.const_missing(name)
|
22
|
+
key = name.to_s.downcase
|
23
|
+
gemfile[key] || profile[key] || super(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
name : neapolitan
|
2
|
+
date : 2010-09-14
|
3
|
+
version : 0.2.0
|
4
|
+
|
5
|
+
requires:
|
6
|
+
- malt
|
7
|
+
- rdiscount (optional, development)
|
8
|
+
- redcloth (optional, development)
|
9
|
+
- haml (optional, development)
|
10
|
+
- rdoc 2.5+ (optional, development, document)
|
11
|
+
- syckle (build)
|
12
|
+
- qed (test)
|
13
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
title : Neapolitan
|
2
|
+
summary : Kid in the Candy Store Templating
|
3
|
+
copyright: Copyright (c) 2010 Thomas Sawyer
|
4
|
+
license : Apache 2.0
|
5
|
+
suite : rubyworks
|
6
|
+
contact : rubyworks-mailinglist@googlegroups.com
|
7
|
+
|
8
|
+
description:
|
9
|
+
Neapolitan is a meta-templating engine. Like a candy store
|
10
|
+
it allows you to pick and choose from a variety of rendering
|
11
|
+
formats in the construction of a single document. Selections
|
12
|
+
include eruby, textile, markdown and many others.
|
13
|
+
|
14
|
+
authors:
|
15
|
+
- Thomas Sawyer
|
16
|
+
|
17
|
+
resources:
|
18
|
+
homepage : http://rubyworks.github.com/neapolitan
|
19
|
+
development : http://github.com/rubyworks/neapolitan
|
20
|
+
reference : http://rubyworks.github.com/neapolitan/docs/api
|
21
|
+
wiki : http://wiki.github.com/rubyworks/neapolitan
|
22
|
+
repository : git://github.com/rubyworks/neapolitan.git
|
23
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'neapolitan/factory'
|
2
|
+
|
3
|
+
module Neapolitan
|
4
|
+
|
5
|
+
# A Part is the section of a page. Pages can be segmented into
|
6
|
+
# parts using the '--- FORMAT' notation.
|
7
|
+
class Part
|
8
|
+
|
9
|
+
# Markup format (html, rdoc, markdown, textile)
|
10
|
+
attr :formats
|
11
|
+
|
12
|
+
# Body of text as given in the part.
|
13
|
+
attr :text
|
14
|
+
|
15
|
+
#
|
16
|
+
def initialize(text, *formats)
|
17
|
+
@text = text
|
18
|
+
@formats = formats
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
def render(data, &block)
|
23
|
+
formats.inject(text) do |rendering, format|
|
24
|
+
factory.render(format, rendering, data, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
def factory
|
30
|
+
Factory
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'neapolitan/part'
|
2
|
+
|
3
|
+
module Neapolitan
|
4
|
+
|
5
|
+
#
|
6
|
+
class Template
|
7
|
+
|
8
|
+
# Template text.
|
9
|
+
attr :text
|
10
|
+
|
11
|
+
# Header data, also known as <i>front matter</i>.
|
12
|
+
attr :header
|
13
|
+
|
14
|
+
# Templating format to apply "whole-clothe".
|
15
|
+
attr :stencil
|
16
|
+
|
17
|
+
# Default format(s) for undecorated parts.
|
18
|
+
# If not set defaults to 'html'.
|
19
|
+
attr :default
|
20
|
+
|
21
|
+
## Output extension (defualt is 'html')
|
22
|
+
#attr :extension
|
23
|
+
|
24
|
+
# Provide template +text+, +data+ and yield +block+.
|
25
|
+
def initialize(text, options={})
|
26
|
+
@text = text
|
27
|
+
@stencil = options[:stencil]
|
28
|
+
@default = [options[:default] || 'html'].flatten
|
29
|
+
@parts = []
|
30
|
+
parse
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
def inspect
|
35
|
+
"<#{self.class}: @text='#{text[0,10]}'>"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Unrendered template parts.
|
39
|
+
def parts
|
40
|
+
@parts
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
def render(data={}, &block)
|
45
|
+
Rendering.new(self, data, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
#
|
51
|
+
def parse
|
52
|
+
sect = text.split(/^\-\-\-/)
|
53
|
+
|
54
|
+
if sect.size == 1
|
55
|
+
@header = {}
|
56
|
+
@parts << Part.new(sect[0], *[@stencil, @default].compact.flatten)
|
57
|
+
else
|
58
|
+
sect.shift if sect.first.strip.empty?
|
59
|
+
#void = sect.shift if sect.first.strip.empty?
|
60
|
+
head = sect.shift
|
61
|
+
head = YAML::load(head)
|
62
|
+
parse_header(head)
|
63
|
+
|
64
|
+
sect.each do |body|
|
65
|
+
index = body.index("\n")
|
66
|
+
formats = body[0...index].strip
|
67
|
+
formats = formats.split(/\s+/) if String===formats
|
68
|
+
formats = @default if formats.empty?
|
69
|
+
formats << @stencil if @stencil
|
70
|
+
text = body[index+1..-1]
|
71
|
+
@parts << Part.new(text, *formats)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
def parse_header(head)
|
78
|
+
@header = head
|
79
|
+
@stencil = head.delete('stencil'){ @stencil }
|
80
|
+
@default = head.delete('default'){ @default }
|
81
|
+
#@extension = head.delete('extension'){ @extension }
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
# Template Rendering
|
87
|
+
class Rendering
|
88
|
+
|
89
|
+
#
|
90
|
+
attr :template
|
91
|
+
|
92
|
+
# Source of data, can either be an
|
93
|
+
# Hash, Binding or Object.
|
94
|
+
attr :data
|
95
|
+
|
96
|
+
#
|
97
|
+
attr :block
|
98
|
+
|
99
|
+
#
|
100
|
+
def initialize(template, data, &block)
|
101
|
+
@template = template
|
102
|
+
@data = data
|
103
|
+
@block = block
|
104
|
+
|
105
|
+
if !@block
|
106
|
+
case data
|
107
|
+
when Hash
|
108
|
+
yld = data.delete('yield')
|
109
|
+
@block = Proc.new{ yld } if yld
|
110
|
+
end
|
111
|
+
@block = Proc.new{''} unless @block
|
112
|
+
end
|
113
|
+
|
114
|
+
render
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_s
|
118
|
+
#render unless @output
|
119
|
+
@output
|
120
|
+
end
|
121
|
+
|
122
|
+
# Renderings of each part.
|
123
|
+
def to_a
|
124
|
+
#render unless @output
|
125
|
+
@renders
|
126
|
+
end
|
127
|
+
|
128
|
+
# Summary is the rendering of the first part.
|
129
|
+
def summary
|
130
|
+
#render unless @output
|
131
|
+
@summary
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
def header
|
136
|
+
@template.header
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def render
|
142
|
+
@renders = @template.parts.map{ |part| part.render(@data, &@block) }
|
143
|
+
@summary = @renders.first
|
144
|
+
@output = @renders.join("\n")
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
data/meta/data.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Object.__send__(:remove_const, :VERSION) if Object.const_defined?(:VERSION) # becuase Ruby 1.8~ gets in the way
|
2
|
+
|
3
|
+
module Neapolitan
|
4
|
+
|
5
|
+
DIRECTORY = File.dirname(__FILE__)
|
6
|
+
|
7
|
+
def self.gemfile
|
8
|
+
@gemfile ||= (
|
9
|
+
require 'yaml'
|
10
|
+
YAML.load(File.new(DIRECTORY + '/gemfile'))
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.profile
|
15
|
+
@profile ||= (
|
16
|
+
require 'yaml'
|
17
|
+
YAML.load(File.new(DIRECTORY + '/profile'))
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.const_missing(name)
|
22
|
+
key = name.to_s.downcase
|
23
|
+
gemfile[key] || profile[key] || super(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
data/meta/gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
name : neapolitan
|
2
|
+
date : 2010-09-14
|
3
|
+
version : 0.2.0
|
4
|
+
|
5
|
+
requires:
|
6
|
+
- malt
|
7
|
+
- rdiscount (optional, development)
|
8
|
+
- redcloth (optional, development)
|
9
|
+
- haml (optional, development)
|
10
|
+
- rdoc 2.5+ (optional, development, document)
|
11
|
+
- syckle (build)
|
12
|
+
- qed (test)
|
13
|
+
|
data/meta/profile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
title : Neapolitan
|
2
|
+
summary : Kid in the Candy Store Templating
|
3
|
+
copyright: Copyright (c) 2010 Thomas Sawyer
|
4
|
+
license : Apache 2.0
|
5
|
+
suite : rubyworks
|
6
|
+
contact : rubyworks-mailinglist@googlegroups.com
|
7
|
+
|
8
|
+
description:
|
9
|
+
Neapolitan is a meta-templating engine. Like a candy store
|
10
|
+
it allows you to pick and choose from a variety of rendering
|
11
|
+
formats in the construction of a single document. Selections
|
12
|
+
include eruby, textile, markdown and many others.
|
13
|
+
|
14
|
+
authors:
|
15
|
+
- Thomas Sawyer
|
16
|
+
|
17
|
+
resources:
|
18
|
+
homepage : http://rubyworks.github.com/neapolitan
|
19
|
+
development : http://github.com/rubyworks/neapolitan
|
20
|
+
reference : http://rubyworks.github.com/neapolitan/docs/api
|
21
|
+
wiki : http://wiki.github.com/rubyworks/neapolitan
|
22
|
+
repository : git://github.com/rubyworks/neapolitan.git
|
23
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<h1>Yummy Choclate </h1>
|
2
|
+
<p>
|
3
|
+
Hi Ginger,
|
4
|
+
</p>
|
5
|
+
<p>
|
6
|
+
I know you want some of that yummy stuff.
|
7
|
+
</p>
|
8
|
+
|
9
|
+
<div class="CodeRay">
|
10
|
+
<div class="code"><pre>
|
11
|
+
<span style="background-color:#fff0f0;color:#D20"><span style="color:#710">%{</span><span style="">c h o c o l a t e s</span><span style="color:#710">}</span></span>.each <span style="color:#080;font-weight:bold">do</span> |letter|
|
12
|
+
puts <span style="background-color:#fff0f0;color:#D20"><span style="color:#710">"</span><span style="">Give me a </span><span style="background:#ddd;color:black"><span style="background:#ddd;font-weight:bold;color:#666">#{</span>letter<span style="background:#ddd;font-weight:bold;color:#666">}</span></span><span style="">!</span><span style="color:#710">"</span></span>
|
13
|
+
<span style="color:#080;font-weight:bold">end</span>
|
14
|
+
|
15
|
+
puts <span style="background-color:#fff0f0;color:#D20"><span style="color:#710">"</span><span style="">What's that spell?</span><span style="color:#710">"</span></span>
|
16
|
+
|
17
|
+
</pre></div>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
|
21
|
+
<quote>
|
22
|
+
What can I say?
|
23
|
+
|
24
|
+
</quote>
|
25
|
+
|
26
|
+
|
27
|
+
<table>
|
28
|
+
<tr>
|
29
|
+
<td> </td>
|
30
|
+
<td> 2009 </td>
|
31
|
+
<td> 2010 </td>
|
32
|
+
</tr>
|
33
|
+
<tr>
|
34
|
+
<td> Has Choclates? </td>
|
35
|
+
<td> No </td>
|
36
|
+
<td> Yes! </td>
|
37
|
+
</tr>
|
38
|
+
</table>
|
39
|
+
<p>As you can see. It's all <em>fun</em> and <em>games</em> here.</p>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
extension: html
|
2
|
+
|
3
|
+
--- rdoc erb
|
4
|
+
|
5
|
+
= Yummy Choclate
|
6
|
+
|
7
|
+
Hi <%= name %>,
|
8
|
+
|
9
|
+
I know you want some of that yummy stuff.
|
10
|
+
|
11
|
+
--- coderay.ruby
|
12
|
+
|
13
|
+
%{c h o c o l a t e s}.each do |letter|
|
14
|
+
puts "Give me a #{letter}!"
|
15
|
+
end
|
16
|
+
|
17
|
+
puts "What's that spell?"
|
18
|
+
|
19
|
+
--- html liquid
|
20
|
+
|
21
|
+
<quote>
|
22
|
+
{{ yield }}
|
23
|
+
</quote>
|
24
|
+
|
25
|
+
--- textile
|
26
|
+
|
27
|
+
| | 2009 | 2010 |
|
28
|
+
| Has Choclates? | No | Yes! |
|
29
|
+
|
30
|
+
--- markdown
|
31
|
+
|
32
|
+
As you can see. It's all _fun_ and _games_ here.
|
33
|
+
|
data/qed/overview.rdoc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
= Example Neapolitan Document
|
2
|
+
|
3
|
+
Here is an example neapolitan file, 'example.choc':
|
4
|
+
|
5
|
+
extension: html
|
6
|
+
|
7
|
+
--- rdoc erb
|
8
|
+
|
9
|
+
= Yummy Vanilla
|
10
|
+
|
11
|
+
Hi <%= name %>,
|
12
|
+
|
13
|
+
I know you want some of that yummy stuff.
|
14
|
+
|
15
|
+
--- coderay.ruby
|
16
|
+
|
17
|
+
%{S t r a w b e r r y}.each do |letter|
|
18
|
+
puts "Give me a #{letter}!"
|
19
|
+
end
|
20
|
+
|
21
|
+
puts "What's that spell?"
|
22
|
+
|
23
|
+
--- html liquid
|
24
|
+
|
25
|
+
<quote>
|
26
|
+
{{ yield }}
|
27
|
+
</quote>
|
28
|
+
|
29
|
+
--- textile
|
30
|
+
|
31
|
+
| | 2009 | 2010 |
|
32
|
+
| Has Choclate? | No | Yes! |
|
33
|
+
|
34
|
+
--- markdown
|
35
|
+
|
36
|
+
As you can see. It's all _fun_ and _games_ here.
|
37
|
+
|
38
|
+
= Loading the Library
|
39
|
+
|
40
|
+
Require the library.
|
41
|
+
|
42
|
+
require 'neapolitan'
|
43
|
+
|
44
|
+
= Reading a Neapolitan File
|
45
|
+
|
46
|
+
Load our example template[fixtures/example.npt].
|
47
|
+
|
48
|
+
file = "qed/fixtures/example.npt"
|
49
|
+
|
50
|
+
document = Neapolitan::Document.new(file)
|
51
|
+
|
52
|
+
= Rendering Data Sources
|
53
|
+
|
54
|
+
Neapolitan uses Malt on the backend. Malt supports a three separate ways to pass
|
55
|
+
data into a template.
|
56
|
+
|
57
|
+
The most obvious data source is a Hash.
|
58
|
+
|
59
|
+
data = {:name=>"Tom"}
|
60
|
+
|
61
|
+
text = document.render(data).to_s
|
62
|
+
|
63
|
+
text.assert =~ /Hi Tom/
|
64
|
+
|
65
|
+
Templates can also be rendered given a Binding.
|
66
|
+
|
67
|
+
name = "Huck"
|
68
|
+
|
69
|
+
text = document.render(binding).to_s
|
70
|
+
|
71
|
+
text.assert =~ /Hi Huck/
|
72
|
+
|
73
|
+
And lastly, they can be renderedwith the scope of any other type of Object,
|
74
|
+
including an instance of a Struct.
|
75
|
+
|
76
|
+
scope = Struct.new(:name).new("Becky")
|
77
|
+
|
78
|
+
text = document.render(scope).to_s
|
79
|
+
|
80
|
+
text.assert =~ /Hi Becky/
|
81
|
+
|
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: neapolitan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Thomas Sawyer
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-09-14 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: malt
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rdiscount
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: redcloth
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: haml
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: rdoc
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 9
|
86
|
+
segments:
|
87
|
+
- 2
|
88
|
+
- 5
|
89
|
+
version: "2.5"
|
90
|
+
type: :development
|
91
|
+
version_requirements: *id005
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
name: syckle
|
94
|
+
prerelease: false
|
95
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
type: :development
|
105
|
+
version_requirements: *id006
|
106
|
+
- !ruby/object:Gem::Dependency
|
107
|
+
name: qed
|
108
|
+
prerelease: false
|
109
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
hash: 3
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
version: "0"
|
118
|
+
type: :development
|
119
|
+
version_requirements: *id007
|
120
|
+
description: Neapolitan is a meta-templating engine. Like a candy store it allows you to pick and choose from a variety of rendering formats in the construction of a single document. Selections include eruby, textile, markdown and many others.
|
121
|
+
email: rubyworks-mailinglist@googlegroups.com
|
122
|
+
executables:
|
123
|
+
- neapolitan
|
124
|
+
extensions: []
|
125
|
+
|
126
|
+
extra_rdoc_files:
|
127
|
+
- README.rdoc
|
128
|
+
files:
|
129
|
+
- bin/neapolitan
|
130
|
+
- lib/neapolitan/command.rb
|
131
|
+
- lib/neapolitan/config.rb
|
132
|
+
- lib/neapolitan/document.rb
|
133
|
+
- lib/neapolitan/factory.rb
|
134
|
+
- lib/neapolitan/meta/data.rb
|
135
|
+
- lib/neapolitan/meta/gemfile
|
136
|
+
- lib/neapolitan/meta/profile
|
137
|
+
- lib/neapolitan/part.rb
|
138
|
+
- lib/neapolitan/template.rb
|
139
|
+
- lib/neapolitan.rb
|
140
|
+
- meta/data.rb
|
141
|
+
- meta/gemfile
|
142
|
+
- meta/profile
|
143
|
+
- qed/fixtures/example.html
|
144
|
+
- qed/fixtures/example.npt
|
145
|
+
- qed/fixtures/example.yaml
|
146
|
+
- qed/overview.rdoc
|
147
|
+
- HISTORY.rdoc
|
148
|
+
- LICENSE
|
149
|
+
- README.rdoc
|
150
|
+
has_rdoc: true
|
151
|
+
homepage: http://rubyworks.github.com/neapolitan
|
152
|
+
licenses:
|
153
|
+
- Apache 2.0
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options:
|
156
|
+
- --title
|
157
|
+
- Neapolitan API
|
158
|
+
- --main
|
159
|
+
- README.rdoc
|
160
|
+
require_paths:
|
161
|
+
- lib
|
162
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
163
|
+
none: false
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
hash: 3
|
168
|
+
segments:
|
169
|
+
- 0
|
170
|
+
version: "0"
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
none: false
|
173
|
+
requirements:
|
174
|
+
- - ">="
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
hash: 3
|
177
|
+
segments:
|
178
|
+
- 0
|
179
|
+
version: "0"
|
180
|
+
requirements: []
|
181
|
+
|
182
|
+
rubyforge_project: neapolitan
|
183
|
+
rubygems_version: 1.3.7
|
184
|
+
signing_key:
|
185
|
+
specification_version: 3
|
186
|
+
summary: Kid in the Candy Store Templating
|
187
|
+
test_files: []
|
188
|
+
|