neapolitan 0.3.0 → 0.4.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/.ruby +72 -0
- data/HISTORY.rdoc +17 -1
- data/LICENSE.txt +29 -0
- data/README.rdoc +10 -8
- data/demo/01_overview.rdoc +86 -0
- data/demo/02_faq.rdoc +51 -0
- data/demo/applique/env.rb +12 -0
- data/demo/fixtures/example.html +39 -0
- data/demo/fixtures/example.np +31 -0
- data/demo/fixtures/example.yaml +5 -0
- data/lib/neapolitan/cli.rb +66 -0
- data/lib/neapolitan/core_ext.rb +2 -0
- data/lib/neapolitan/factory.rb +123 -0
- data/lib/neapolitan/part.rb +89 -0
- data/lib/neapolitan/rendering.rb +39 -0
- data/lib/neapolitan/template.rb +269 -0
- data/lib/neapolitan/version.rb +17 -0
- data/lib/neapolitan.rb +57 -406
- data/lib/neapolitan.yml +72 -0
- metadata +121 -123
- data/LICENSE +0 -205
- data/lib/neapolitan/meta/data.rb +0 -26
- data/lib/neapolitan/meta/package +0 -12
- data/lib/neapolitan/meta/profile +0 -23
- data/meta/data.rb +0 -26
- data/meta/package +0 -12
- data/meta/profile +0 -23
data/lib/neapolitan.rb
CHANGED
@@ -1,424 +1,75 @@
|
|
1
|
-
require 'neapolitan/meta/data'
|
2
|
-
#require 'tilt'
|
3
|
-
require 'malt'
|
4
|
-
|
5
1
|
module Neapolitan
|
6
2
|
|
3
|
+
if RUBY_VERSION > '1.9'
|
4
|
+
require_relative 'neapolitan/version'
|
5
|
+
require_relative 'neapolitan/core_ext'
|
6
|
+
require_relative 'neapolitan/template'
|
7
|
+
require_relative 'neapolitan/part'
|
8
|
+
require_relative 'neapolitan/rendering'
|
9
|
+
require_relative 'neapolitan/factory'
|
10
|
+
require_relative 'neapolitan/cli'
|
11
|
+
else
|
12
|
+
require 'neapolitan/version'
|
13
|
+
require 'neapolitan/core_ext'
|
14
|
+
require 'neapolitan/template'
|
15
|
+
require 'neapolitan/part'
|
16
|
+
require 'neapolitan/factory'
|
17
|
+
require 'neapolitan/rendering'
|
18
|
+
require 'neapolitan/cli'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set default rendering system for all templates.
|
22
|
+
# This can either be `:tilt` or `:malt`, the default.
|
23
|
+
def self.system(libname=nil)
|
24
|
+
@system = libname if libname
|
25
|
+
@system
|
26
|
+
end
|
27
|
+
|
28
|
+
# Limit the section formats for all templates to the
|
29
|
+
# sepecified selection via a selection procedure.
|
30
|
+
def self.select(&select)
|
31
|
+
@select = select if select
|
32
|
+
@select
|
33
|
+
end
|
34
|
+
|
35
|
+
# Limit the section formats for all templates via
|
36
|
+
# a rejection procedure.
|
37
|
+
def self.reject(&reject)
|
38
|
+
@reject = reject if reject
|
39
|
+
@reject
|
40
|
+
end
|
41
|
+
|
42
|
+
# Load template from given source.
|
43
|
+
#
|
44
|
+
# @param [File,IO,String] source
|
45
|
+
# The document to render.
|
46
|
+
#
|
47
|
+
# @param [Hash] options
|
48
|
+
# Rendering options.
|
7
49
|
#
|
8
50
|
def self.load(source, options={})
|
9
51
|
Template.new(source, options)
|
10
52
|
end
|
11
53
|
|
54
|
+
# Specifically create a new template from a text string.
|
55
|
+
#
|
56
|
+
# @param [#to_s] source
|
57
|
+
# The document to render.
|
58
|
+
#
|
59
|
+
# @param [Hash] options
|
60
|
+
# Rendering options.
|
12
61
|
#
|
13
62
|
def self.text(source, options={})
|
14
63
|
Template.new(source.to_s, options)
|
15
64
|
end
|
16
65
|
|
66
|
+
# Specifically create a new template from a file, given the files name.
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# Neapolitan::Template.file('example.np')
|
17
70
|
#
|
18
71
|
def self.file(fname, options={})
|
19
72
|
Template.new(File.new(fname), options)
|
20
73
|
end
|
21
74
|
|
22
|
-
#
|
23
|
-
class Template
|
24
|
-
|
25
|
-
# Template text.
|
26
|
-
attr :text
|
27
|
-
|
28
|
-
# File name of template, if given.
|
29
|
-
attr :file
|
30
|
-
|
31
|
-
# Templating format to apply "whole-clothe".
|
32
|
-
attr :stencil
|
33
|
-
|
34
|
-
# Default format(s) for undecorated parts.
|
35
|
-
# If not otherwise set the default is 'html'.
|
36
|
-
attr :default
|
37
|
-
|
38
|
-
# Common format(s) to be applied to all parts.
|
39
|
-
# These are applied at the end.
|
40
|
-
attr :common
|
41
|
-
|
42
|
-
# Rendering formats to disallow.
|
43
|
-
#attr :reject
|
44
|
-
|
45
|
-
# Header data, also known as <i>front matter</i>.
|
46
|
-
attr :header
|
47
|
-
|
48
|
-
#
|
49
|
-
def initialize(source, options={})
|
50
|
-
case source
|
51
|
-
when ::File
|
52
|
-
@file = source.path #name
|
53
|
-
@text = source.read
|
54
|
-
source.close
|
55
|
-
when ::IO
|
56
|
-
@text = source.read
|
57
|
-
@file = options[:file]
|
58
|
-
source.close
|
59
|
-
when ::String
|
60
|
-
@text = source
|
61
|
-
@file = options[:file]
|
62
|
-
when Hash
|
63
|
-
options = source
|
64
|
-
source = nil
|
65
|
-
@file = options[:file]
|
66
|
-
@text = File.read(@file)
|
67
|
-
end
|
68
|
-
|
69
|
-
@stencil = options[:stencil]
|
70
|
-
@default = options[:default] || 'html'
|
71
|
-
@common = options[:common]
|
72
|
-
#@reject = options[:reject]
|
73
|
-
|
74
|
-
parse
|
75
|
-
end
|
76
|
-
|
77
|
-
#
|
78
|
-
def inspect
|
79
|
-
if file
|
80
|
-
"<#{self.class}: @file='#{file}'>"
|
81
|
-
else
|
82
|
-
"<#{self.class}: @text='#{text[0,10]}'>"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Rejection formats.
|
87
|
-
#--
|
88
|
-
# TODO: filter common and default
|
89
|
-
#++
|
90
|
-
def reject(&block)
|
91
|
-
parts.each do |part|
|
92
|
-
part.formats.reject!(&block)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# Select formats.
|
97
|
-
#--
|
98
|
-
# TODO: filter common and default
|
99
|
-
#++
|
100
|
-
def select(&block)
|
101
|
-
parts.each do |part|
|
102
|
-
part.formats.select!(&block)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Unrendered template parts.
|
107
|
-
def parts
|
108
|
-
@parts
|
109
|
-
end
|
110
|
-
|
111
|
-
#
|
112
|
-
def render(data={}, &block)
|
113
|
-
if !block
|
114
|
-
case data
|
115
|
-
when Hash
|
116
|
-
yld = data.delete('yield')
|
117
|
-
block = Proc.new{ yld } if yld
|
118
|
-
end
|
119
|
-
block = Proc.new{''} unless block
|
120
|
-
end
|
121
|
-
|
122
|
-
renders = parts.map{ |part| render_part(part, data, &block) }
|
123
|
-
|
124
|
-
Rendering.new(renders, header)
|
125
|
-
end
|
126
|
-
|
127
|
-
# :call-seq:
|
128
|
-
# save(data={}, &block)
|
129
|
-
# save(file, data={}, &block)
|
130
|
-
#
|
131
|
-
def save(*args, &block)
|
132
|
-
data = Hash===args.last ? args.pop : {}
|
133
|
-
path = args.first
|
134
|
-
|
135
|
-
rendering = render(data, &block)
|
136
|
-
|
137
|
-
path = path || rendering.header['output']
|
138
|
-
path = path || path.chomp(File.extname(file))
|
139
|
-
|
140
|
-
path = Dir.pwd unless path
|
141
|
-
if File.directory?(path)
|
142
|
-
file = File.join(path, file.chomp(File.extname(file)) + extension)
|
143
|
-
else
|
144
|
-
file = path
|
145
|
-
end
|
146
|
-
|
147
|
-
if $DRYRUN
|
148
|
-
$stderr << "[DRYRUN] write #{fname}"
|
149
|
-
else
|
150
|
-
File.open(file, 'w'){ |f| f << rendering.to_s }
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
private
|
155
|
-
|
156
|
-
#--
|
157
|
-
# TODO: Should a stencil be applied once to the entire document?
|
158
|
-
#++
|
159
|
-
def parse
|
160
|
-
@parts = []
|
161
|
-
|
162
|
-
sect = text.split(/^\-\-\-/)
|
163
|
-
|
164
|
-
if sect.size == 1
|
165
|
-
@header = {}
|
166
|
-
@parts << Part.new(sect[0]) #, *[@stencil, @default].compact.flatten)
|
167
|
-
else
|
168
|
-
sect.shift if sect.first.strip.empty?
|
169
|
-
|
170
|
-
head = sect.shift
|
171
|
-
head = YAML::load(head)
|
172
|
-
parse_header(head)
|
173
|
-
|
174
|
-
sect.each do |body|
|
175
|
-
index = body.index("\n")
|
176
|
-
formats = body[0...index].strip
|
177
|
-
formats = formats.split(/\s+/) if String===formats
|
178
|
-
#formats = @default if formats.empty?
|
179
|
-
#formats.unshift(@stencil) if @stencil
|
180
|
-
text = body[index+1..-1]
|
181
|
-
@parts << Part.new(text, *formats)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
#
|
187
|
-
def parse_header(head)
|
188
|
-
@header = head
|
189
|
-
@default = head.delete('default'){ @default }
|
190
|
-
@common = head.delete('common'){ @common }
|
191
|
-
end
|
192
|
-
|
193
|
-
#
|
194
|
-
def render_part(part, data, &block)
|
195
|
-
formats = part.formats
|
196
|
-
formats = default if formats.empty?
|
197
|
-
formats = [formats, common].flatten.compact
|
198
|
-
|
199
|
-
#case reject
|
200
|
-
#when Array
|
201
|
-
# formats = formats - reject.map{|r|r.to_s}
|
202
|
-
#when Proc
|
203
|
-
# formats = formats.reject(&reject)
|
204
|
-
#end
|
205
|
-
|
206
|
-
formats = [stencil, *formats].compact
|
207
|
-
|
208
|
-
formats.inject(part.text) do |text, format|
|
209
|
-
factory.render(text, format, data, &block)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
#
|
214
|
-
def factory
|
215
|
-
@factory ||= Factory.new
|
216
|
-
end
|
217
|
-
|
218
|
-
end
|
219
|
-
|
220
|
-
# A part is a section of a template. Templates can be segmented into
|
221
|
-
# parts using the '--- FORMAT' notation.
|
222
|
-
class Part
|
223
|
-
# Rendering formats (html, rdoc, markdown, textile, etc.)
|
224
|
-
attr :formats
|
225
|
-
|
226
|
-
# Body of text as given in the part.
|
227
|
-
attr :text
|
228
|
-
|
229
|
-
#
|
230
|
-
def initialize(text, *formats)
|
231
|
-
@text = text
|
232
|
-
@formats = formats
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
# Template Rendering
|
237
|
-
class Rendering
|
238
|
-
#
|
239
|
-
def initialize(renders, header)
|
240
|
-
@renders = renders
|
241
|
-
@summary = renders.first
|
242
|
-
@output = renders.join("\n")
|
243
|
-
@header = header
|
244
|
-
end
|
245
|
-
|
246
|
-
#
|
247
|
-
def to_s
|
248
|
-
@output
|
249
|
-
end
|
250
|
-
|
251
|
-
# Renderings of each part.
|
252
|
-
def to_a
|
253
|
-
@renders
|
254
|
-
end
|
255
|
-
|
256
|
-
# Summary is the rendering of the first part.
|
257
|
-
def summary
|
258
|
-
@summary
|
259
|
-
end
|
260
|
-
|
261
|
-
#
|
262
|
-
def header
|
263
|
-
@header
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
# Controls rendering to a variety of back-end templating
|
268
|
-
# and markup systems via Malt.
|
269
|
-
class Factory
|
270
|
-
#
|
271
|
-
attr :types
|
272
|
-
|
273
|
-
#
|
274
|
-
def initialize(options={})
|
275
|
-
@types = options[:types]
|
276
|
-
end
|
277
|
-
|
278
|
-
#
|
279
|
-
def render(text, format, data, &yld)
|
280
|
-
case format
|
281
|
-
when /^coderay/
|
282
|
-
coderay(text, format)
|
283
|
-
when /^syntax/
|
284
|
-
syntax(text, format)
|
285
|
-
else
|
286
|
-
render_via_malt(text, format, data, &yld)
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
#
|
291
|
-
def render_via_malt(text, format, data, &yld)
|
292
|
-
doc = malt.text(text, :type=>format.to_sym)
|
293
|
-
doc.render(data, &yld)
|
294
|
-
end
|
295
|
-
|
296
|
-
#
|
297
|
-
def render_via_tilt(text, format, data, &yld)
|
298
|
-
if engine = Tilt[format]
|
299
|
-
case data
|
300
|
-
when Hash
|
301
|
-
scope = Object.new
|
302
|
-
table = data
|
303
|
-
when Binding
|
304
|
-
scope = data.eval('self')
|
305
|
-
table = {}
|
306
|
-
else # object scope
|
307
|
-
scope = data
|
308
|
-
table = {}
|
309
|
-
end
|
310
|
-
engine.new{text}.render(scope, table, &yld)
|
311
|
-
else
|
312
|
-
text
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
#
|
317
|
-
def malt
|
318
|
-
@malt ||= (
|
319
|
-
if types && !types.empty?
|
320
|
-
Malt::Machine.new(:types=>types)
|
321
|
-
else
|
322
|
-
Malt::Machine.new
|
323
|
-
end
|
324
|
-
)
|
325
|
-
end
|
326
|
-
|
327
|
-
def coderay(input, format)
|
328
|
-
require 'coderay'
|
329
|
-
format = format.split('.')[1] || :ruby #:plaintext
|
330
|
-
tokens = CodeRay.scan(input, format.to_sym) #:ruby
|
331
|
-
tokens.div()
|
332
|
-
end
|
333
|
-
|
334
|
-
#
|
335
|
-
def syntax(input, format)
|
336
|
-
require 'syntax/convertors/html'
|
337
|
-
format = format.split('.')[1] || 'ruby' #:plaintext
|
338
|
-
lines = true
|
339
|
-
conv = Syntax::Convertors::HTML.for_syntax(format)
|
340
|
-
conv.convert(input,lines)
|
341
|
-
end
|
342
|
-
|
343
|
-
#
|
344
|
-
#def render_stencil(stencil, text, attributes)
|
345
|
-
# case stencil
|
346
|
-
# when 'rhtml'
|
347
|
-
# erb(text, attributes)
|
348
|
-
# when 'liquid'
|
349
|
-
# liquid(text, attributes)
|
350
|
-
# else
|
351
|
-
# text
|
352
|
-
# end
|
353
|
-
#end
|
354
|
-
|
355
|
-
public
|
356
|
-
|
357
|
-
#
|
358
|
-
def self.render(text, format, data, &yld)
|
359
|
-
new.render(text, format, data, &yld)
|
360
|
-
end
|
361
|
-
|
362
|
-
end
|
363
|
-
|
364
|
-
# Command line interface.
|
365
|
-
def self.cli(*argv)
|
366
|
-
options = {}
|
367
|
-
|
368
|
-
option_parser(options).parse!(argv)
|
369
|
-
|
370
|
-
if src = options[:source]
|
371
|
-
data = YAML.load(File.new(src))
|
372
|
-
else
|
373
|
-
data = {} #@source ||= YAML.load(STDIN.read)
|
374
|
-
end
|
375
|
-
|
376
|
-
files = argv
|
377
|
-
|
378
|
-
begin
|
379
|
-
files.each do |file|
|
380
|
-
template = Template.new(File.new(file))
|
381
|
-
if options[:output]
|
382
|
-
#template.save(data)
|
383
|
-
else
|
384
|
-
puts template.render(data)
|
385
|
-
end
|
386
|
-
end
|
387
|
-
rescue => e
|
388
|
-
$DEBUG ? raise(e) : puts(e.message)
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
#--
|
393
|
-
# TODO: Save to output.
|
394
|
-
#++
|
395
|
-
def self.option_parser(options)
|
396
|
-
require 'optparse'
|
397
|
-
|
398
|
-
OptionParser.new do |opt|
|
399
|
-
opt.banner = "neapolitan [file1 file2 ...]"
|
400
|
-
#opt.on("--output", "-o [PATH]", "save output to specified directory") do |path|
|
401
|
-
# options[:output] = path
|
402
|
-
#end
|
403
|
-
opt.on("--source", "-s [FILE]", "data souce (YAML file)") do |file|
|
404
|
-
options[:source] = file
|
405
|
-
end
|
406
|
-
opt.on("--trace", "show extra operational information") do
|
407
|
-
$TRACE = true
|
408
|
-
end
|
409
|
-
opt.on("--dryrun", "-n", "don't actually write to disk") do
|
410
|
-
$DRYRUN = true
|
411
|
-
end
|
412
|
-
opt.on("--debug", "run in debug mode") do
|
413
|
-
$DEBUG = true
|
414
|
-
$VERBOSE = true
|
415
|
-
end
|
416
|
-
opt.on_tail("--help", "display this help message") do
|
417
|
-
puts opt
|
418
|
-
exit
|
419
|
-
end
|
420
|
-
end
|
421
|
-
|
422
|
-
end
|
423
|
-
|
424
75
|
end
|
data/lib/neapolitan.yml
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
---
|
2
|
+
source:
|
3
|
+
- var
|
4
|
+
authors:
|
5
|
+
- name: trans
|
6
|
+
email: transfire@gmail.com
|
7
|
+
copyrights: []
|
8
|
+
replacements: []
|
9
|
+
alternatives: []
|
10
|
+
requirements:
|
11
|
+
- name: malt
|
12
|
+
version: 0.4.0+
|
13
|
+
- name: detroit
|
14
|
+
groups:
|
15
|
+
- build
|
16
|
+
development: true
|
17
|
+
- name: yard
|
18
|
+
groups:
|
19
|
+
- document
|
20
|
+
development: true
|
21
|
+
- name: qed
|
22
|
+
groups:
|
23
|
+
- test
|
24
|
+
development: true
|
25
|
+
- name: coderay
|
26
|
+
groups:
|
27
|
+
- optional
|
28
|
+
- test
|
29
|
+
development: true
|
30
|
+
- name: RedCloth
|
31
|
+
groups:
|
32
|
+
- optional
|
33
|
+
- test
|
34
|
+
development: true
|
35
|
+
- name: rdoc
|
36
|
+
groups:
|
37
|
+
- optional
|
38
|
+
- test
|
39
|
+
development: true
|
40
|
+
- name: liquid
|
41
|
+
groups:
|
42
|
+
- optional
|
43
|
+
- test
|
44
|
+
development: true
|
45
|
+
dependencies: []
|
46
|
+
conflicts: []
|
47
|
+
repositories:
|
48
|
+
- uri: git://github.com/rubyworks/neapolitan.git
|
49
|
+
scm: git
|
50
|
+
name: upstream
|
51
|
+
resources:
|
52
|
+
home: http://rubyworks.github.com/neapolitan
|
53
|
+
code: http://github.com/rubyworks/neapolitan
|
54
|
+
docs: http://rubydoc.info/gems/neapolitan/frames
|
55
|
+
wiki: http://wiki.github.com/rubyworks/neapolitan
|
56
|
+
extra: {}
|
57
|
+
load_path:
|
58
|
+
- lib
|
59
|
+
revision: 0
|
60
|
+
created: '2009-08-25'
|
61
|
+
summary: Kid in the Candy Store Templating
|
62
|
+
title: Neapolitan
|
63
|
+
version: 0.4.0
|
64
|
+
name: neapolitan
|
65
|
+
description: ! 'Neapolitan is a meta-templating engine. Like a candy store it allows
|
66
|
+
you to pick
|
67
|
+
|
68
|
+
and choose from a variety of rendering formats in the construction of a single
|
69
|
+
|
70
|
+
document. Selections include eruby, textile, markdown and many others.'
|
71
|
+
organization: rubyworks
|
72
|
+
date: '2011-11-30'
|