├── var ├── name ├── title ├── version ├── created ├── organization ├── summary ├── authors ├── copyright ├── repositories ├── resources ├── description └── requirements ├── lib ├── neapolitan.yml ├── neapolitan │ ├── core_ext.rb │ ├── version.rb │ ├── rendering.rb │ ├── cli.rb │ ├── part.rb │ ├── factory.rb │ └── template.rb └── neapolitan.rb ├── Gemfile ├── .gitignore ├── bin └── neapolitan ├── demo ├── fixtures │ ├── example.yaml │ ├── example.np │ └── example.html ├── applique │ └── env.rb ├── 02_faq.rdoc └── 01_overview.rdoc ├── work ├── webrite-server ├── trash │ ├── part.rb │ ├── config.rb │ ├── command.rb │ ├── document.rb │ ├── template.rb │ └── factory.rb ├── reference │ ├── temp.rb │ ├── page.rb │ ├── server.rb │ └── generator.rb └── byebye │ └── site.rb ├── .yardopts ├── .travis.yml ├── Assembly ├── MANIFEST ├── README.rdoc ├── LICENSE.txt ├── HISTORY.rdoc ├── .ruby └── .gemspec /var/name: -------------------------------------------------------------------------------- 1 | neapolitan 2 | -------------------------------------------------------------------------------- /var/title: -------------------------------------------------------------------------------- 1 | Neapolitan -------------------------------------------------------------------------------- /var/version: -------------------------------------------------------------------------------- 1 | 0.4.0 2 | -------------------------------------------------------------------------------- /lib/neapolitan.yml: -------------------------------------------------------------------------------- 1 | ../.ruby -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2009-08-25 2 | -------------------------------------------------------------------------------- /var/organization: -------------------------------------------------------------------------------- 1 | rubyworks 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Kid in the Candy Store Templating -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - trans 3 | 4 | -------------------------------------------------------------------------------- /lib/neapolitan/core_ext.rb: -------------------------------------------------------------------------------- 1 | require 'facets/hash/rekey' 2 | 3 | -------------------------------------------------------------------------------- /var/copyright: -------------------------------------------------------------------------------- 1 | --- 2 | - (c) 2010 Rubyworks (BSD-2-Clause) 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ri 2 | log 3 | pkg 4 | tmp 5 | site/docs 6 | work/sandbox 7 | web -------------------------------------------------------------------------------- /var/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git://github.com/rubyworks/neapolitan.git 3 | -------------------------------------------------------------------------------- /bin/neapolitan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'neapolitan' 3 | Neapolitan.cli(*ARGV) 4 | -------------------------------------------------------------------------------- /demo/fixtures/example.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ginger 3 | yield: | 4 | What can I say? 5 | 6 | -------------------------------------------------------------------------------- /work/webrite-server: -------------------------------------------------------------------------------- 1 | #! /usr/bin/ruby 2 | 3 | require 'rage/server' 4 | 5 | Rage::Server.start 6 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --title "Neapolitan" 2 | --readme README.rdoc 3 | --protected 4 | --private 5 | lib 6 | - 7 | [A-Z]*.* 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | script: "bundle exec qed" 3 | rvm: 4 | - 1.8.7 5 | - 1.9.2 6 | - 1.9.3 7 | - ree 8 | - rbx-2.0 9 | - jruby 10 | 11 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/neapolitan 3 | code: http://github.com/rubyworks/neapolitan 4 | docs: http://rubydoc.info/gems/neapolitan/frames 5 | wiki: http://wiki.github.com/rubyworks/neapolitan 6 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | Neapolitan is a meta-templating engine. Like a candy store it allows you to pick 2 | and choose from a variety of rendering formats in the construction of a single 3 | document. Selections include eruby, textile, markdown and many others. 4 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - malt 0.4.0+ 3 | - detroit (build) 4 | - yard (document) 5 | - qed (test) 6 | - coderay (optional, test) 7 | - RedCloth (optional, test) 8 | - rdoc (optional, test) 9 | - liquid (optional, test) 10 | 11 | -------------------------------------------------------------------------------- /demo/applique/env.rb: -------------------------------------------------------------------------------- 1 | directory = File.dirname(__FILE__) 2 | 3 | $LOAD_PATH << directory + '/../lib' 4 | 5 | require 'neapolitan' 6 | 7 | FileUtils.install(Dir[directory + '/../fixtures/*'], '.') 8 | 9 | When "Here is an example Neapolitan template, '(((.*?)))'" do |file, text| 10 | File.open(file, 'w'){ |f| f << text } 11 | end 12 | 13 | -------------------------------------------------------------------------------- /lib/neapolitan/version.rb: -------------------------------------------------------------------------------- 1 | module Neapolitan 2 | 3 | # Access to project metadata. 4 | def self.metadata 5 | @metadata ||= ( 6 | require 'yaml' 7 | YAML.load(File.new(File.dirname(__FILE__) + '/neapolitan.yml')) 8 | ) 9 | end 10 | 11 | # Access project metadata as constants. 12 | def self.const_missing(name) 13 | key = name.to_s.downcase 14 | metadata[key] || super(name) 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | email: 3 | mailto: 4 | - ruby-talk@ruby-lang.org 5 | - rubyworks-mailinglist@googlegroups.com 6 | 7 | github: 8 | gh_pages: web 9 | 10 | gem: 11 | active: true 12 | 13 | qedoc: 14 | files: demo 15 | title: Neapolitan Demonstrandum 16 | output: web/demo.html 17 | 18 | dnote: 19 | labels: ~ 20 | title: Source Notes 21 | output: log/notes.html 22 | 23 | vclog: 24 | output: 25 | - log/changes.html 26 | - log/history.html 27 | 28 | -------------------------------------------------------------------------------- /work/trash/part.rb: -------------------------------------------------------------------------------- 1 | module Neapolitan 2 | 3 | # A part is a section of a template. Templates can be segmented into 4 | # parts using the '--- FORMAT' notation. 5 | class Part 6 | 7 | # Rendering formats (html, rdoc, markdown, textile, etc.) 8 | attr :formats 9 | 10 | # Body of text as given in the part. 11 | attr :text 12 | 13 | # 14 | def initialize(text, *formats) 15 | @text = text 16 | @formats = formats 17 | end 18 | 19 | end 20 | 21 | end 22 | 23 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast .ruby bin demo lib man spec test [A-Z]*.* 2 | .ruby 3 | bin/neapolitan 4 | demo/01_overview.rdoc 5 | demo/02_faq.rdoc 6 | demo/applique/env.rb 7 | demo/fixtures/example.html 8 | demo/fixtures/example.np 9 | demo/fixtures/example.yaml 10 | lib/neapolitan/cli.rb 11 | lib/neapolitan/core_ext.rb 12 | lib/neapolitan/factory.rb 13 | lib/neapolitan/part.rb 14 | lib/neapolitan/rendering.rb 15 | lib/neapolitan/template.rb 16 | lib/neapolitan/version.rb 17 | lib/neapolitan.rb 18 | lib/neapolitan.yml 19 | HISTORY.rdoc 20 | LICENSE.txt 21 | README.rdoc 22 | -------------------------------------------------------------------------------- /demo/fixtures/example.np: -------------------------------------------------------------------------------- 1 | extension: html 2 | 3 | --- erb rdoc 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}.each do |letter| 14 | puts "Give me a #{letter}!" 15 | end 16 | 17 | puts "What's that spell?" 18 | 19 | --- liquid html 20 | 21 | 22 | {{ yield }} 23 | 24 | 25 | --- textile 26 | 27 | | | 2009 | 2010 | 28 | | Has Choclates? | No | Yes! | 29 | 30 | As you can see. It's all _fun_ and _games_ here. 31 | 32 | -------------------------------------------------------------------------------- /work/trash/config.rb: -------------------------------------------------------------------------------- 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 | 31 | -------------------------------------------------------------------------------- /lib/neapolitan/rendering.rb: -------------------------------------------------------------------------------- 1 | module Neapolitan 2 | 3 | # Encapsulates a template rendering. 4 | # 5 | class Rendering 6 | 7 | # 8 | def initialize(renders, metadata) 9 | @renders = renders 10 | @summary = renders.first 11 | @output = renders.join("\n") 12 | @metadata = metadata 13 | end 14 | 15 | # 16 | def to_s 17 | @output 18 | end 19 | 20 | # Renderings of each part. 21 | def to_a 22 | @renders 23 | end 24 | 25 | # Summary is the rendering of the first part. 26 | def summary 27 | @summary 28 | end 29 | 30 | # 31 | def metadata 32 | @metadata 33 | end 34 | 35 | # for temporary backward comptability 36 | alias_method :header, :metadata 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /work/reference/temp.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | 3 | class Context 4 | 5 | def initialize(page) 6 | @page = page 7 | end 8 | 9 | def render(*a) 10 | @page.render(*a) 11 | end 12 | 13 | end 14 | 15 | 16 | class Page 17 | 18 | def initialize 19 | @context = Context.new(self) 20 | @binding = @context.instance_eval{ binding } 21 | end 22 | 23 | def in1 24 | %{ 25 | WAY UP HERE 26 | <%= render('in2') %> 27 | WAY DOWN HERE 28 | } 29 | end 30 | 31 | def in2 32 | %{ 33 | RIGHT UP HERE 34 | <%= render('in3') %> 35 | RIGHT DOWN HERE 36 | } 37 | end 38 | 39 | def in3 40 | "IN THE MIDDLE" 41 | end 42 | 43 | def render(var) 44 | input = eval(var) 45 | template = ERB.new(input) 46 | template.result(binding) 47 | end 48 | 49 | end 50 | 51 | p = Page.new 52 | 53 | puts p.render('in1') 54 | 55 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Neapolitan 2 | 3 | {Homepage}[http://rubyworks.github.com/neapolitan] | 4 | {Development}[http://github.com/rubyworks/neapolitan] | 5 | {Report Issue}[http://github.com/rubyworks/neapolitan/issues] | 6 | {Mailing List}[http://groups.google.com/group/rubyworks-mailinglist] 7 | 8 | {}[http://travis-ci.org/rubyworks/neapolitan] 9 | 10 | 11 | == DESCRIPTION 12 | 13 | There are many markup and templating systems in the 14 | world. Why be limited to just one? Neapolitan gives 15 | you a whole box to pick from. 16 | 17 | 18 | == FEATURES 19 | 20 | * All the variety of a Whitman's Sampler. 21 | * And all the ease of a Hershey's K.I.S.S. 22 | * Website uses pretty colors ;) 23 | 24 | 25 | == SYNOPSIS 26 | 27 | For now, please see the Neapolitan website and online documentation. 28 | 29 | 30 | == HOW TO INSTALL 31 | 32 | You know the routine ;) 33 | 34 | $ sudo gem install neapolitan 35 | 36 | If you're old fashion and want to install to a site 37 | location, see Setup.rb (http://protuils.github.com/setup). 38 | 39 | 40 | == COPYRIGHTS 41 | 42 | Neapolitan, Copyright (c) 2010 Rubyworks 43 | 44 | Neapolitan is distributed under the terms of the *BSD-2-Clause* license. 45 | 46 | THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 47 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 48 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 49 | PURPOSE. 50 | 51 | -------------------------------------------------------------------------------- /demo/fixtures/example.html: -------------------------------------------------------------------------------- 1 |

Yummy Choclate

2 |

3 | Hi Ginger, 4 |

5 |

6 | I know you want some of that yummy stuff. 7 |

8 | 9 |
10 |
11 |   %{c h o c o l a t e}.each do |letter|
12 |     puts "Give me a #{letter}!"
13 |   end
14 | 
15 |   puts "What's that spell?"
16 | 
17 | 
18 |
19 | 20 | 21 | 22 | What can I say? 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
2009 2010
Has Choclates? No Yes!
39 |

As you can see. It's all fun and games here.

40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Neapolitan - Multi-Format Templates 2 | 3 | Copyright 2010 Rubyworks. All rights reserved. 4 | 5 | BSD-2-Cluase License 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 19 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 | COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | (http://rubyworks.github.com/neapolitan) 29 | 30 | -------------------------------------------------------------------------------- /demo/02_faq.rdoc: -------------------------------------------------------------------------------- 1 | = FAQ 2 | 3 | How do I limit the section formats that can be used? 4 | 5 | There are two methods that can be used to limit the formats that 6 | of a Neapolitan template, namely, #select and #reject. 7 | 8 | After creating a Template object, use the #select and/or the #reject 9 | methods to filter out any unwanted formats. 10 | 11 | template = Neapolitan.file('example.np') 12 | 13 | template.reject{ |format| %w{liquid}.include?(format) } 14 | 15 | template.render(:name=>"Tom") 16 | 17 | These methods can be used for more aggressive validation by raising 18 | an error. 19 | 20 | template = Neapolitan.file('example.np') 21 | 22 | template.reject do |format| 23 | raise TypeError if %w{liquid}.include?(format) 24 | false 25 | end 26 | 27 | expect TypeError do 28 | template.render(:name=>"Tome") 29 | end 30 | 31 | Why should template formats be listed before markup format? 32 | 33 | Consider what happens if have a document section proccessed by RDoc 34 | before applying templating such as ERB: 35 | 36 | = Example 37 | 38 | Hi, <%= name %> 39 | 40 | The result never ends up utilizing ERB properly because RDoc transformed 41 | the document into: 42 | 43 |

Example

44 | 45 | Hi, <%= name > 46 | 47 | Therefore you should always list the template format before markup formats. 48 | Of course usually template formats are not used on section by section 49 | basis in anycase, so this won't be an issue, but it's good to know just 50 | in case. 51 | 52 | -------------------------------------------------------------------------------- /HISTORY.rdoc: -------------------------------------------------------------------------------- 1 | = RELEASE HISTORY 2 | 3 | == 0.4.0 / 2011-11-30 4 | 5 | This release updates Neapolitan for use with the latest version 6 | of Malt (v0.4.0). At the same, time the underlying API has been improved. 7 | The API remains compatible with the previous version, with the exception 8 | of one YAML front matter property --the `common` field has been renamed 9 | to `finish`, to better indicate when it is applied during rendering. 10 | 11 | Changes: 12 | 13 | * Rename `common` metadata property to `finish`. 14 | * Add #format block setter for applying complex format rules. 15 | * Apply #select and #reject blocks during rendering instead of before. 16 | * Update part rendering for compatibility with Malt 0.4+. 17 | 18 | 19 | == 0.3.0 / 2010-11-09 20 | 21 | This release entails a fairly major overhaul of the API. Primarily, the 22 | `Document` class has been removed, so the `Template` class now handles 23 | all cases. 24 | 25 | Changes: 26 | 27 | * Deprecate Document class, in favor of single Template class interface. 28 | * Deprecate Command class and move code to class method(s) of Neapolitan. 29 | * Add #select and #reject methods to Template class. 30 | 31 | 32 | == 0.2.0 / 2010-09-14 33 | 34 | This release renames project from "Chocolates" to "Neapolitan". It's also 35 | the first release that is mature enough for general use. 36 | 37 | Changes: 38 | 39 | * Renamed project from "chocolates" to "neapolitan". 40 | 41 | 42 | == 0.1.0 / 2008-10-25 43 | 44 | Not an official release. This is the first usable version of Chocolates. 45 | 46 | Changes: 47 | 48 | * Happy Birthday! 49 | -------------------------------------------------------------------------------- /.ruby: -------------------------------------------------------------------------------- 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' 73 | -------------------------------------------------------------------------------- /lib/neapolitan/cli.rb: -------------------------------------------------------------------------------- 1 | module Neapolitan 2 | 3 | # Command line interface. 4 | def self.cli(*argv) 5 | options = {} 6 | 7 | option_parser(options).parse!(argv) 8 | 9 | if src = options[:source] 10 | data = YAML.load(File.new(src)) 11 | else 12 | data = {} #@source ||= YAML.load(STDIN.read) 13 | end 14 | 15 | files = argv 16 | 17 | begin 18 | files.each do |file| 19 | template = Template.new(File.new(file)) 20 | if options[:output] 21 | #template.save(data) 22 | else 23 | puts template.render(data) 24 | end 25 | end 26 | rescue => e 27 | $DEBUG ? raise(e) : puts(e.message) 28 | end 29 | end 30 | 31 | # TODO: Save to output ? 32 | 33 | # 34 | def self.option_parser(options) 35 | require 'optparse' 36 | 37 | OptionParser.new do |opt| 38 | opt.banner = "neapolitan [file1 file2 ...]" 39 | #opt.on("--output", "-o [PATH]", "save output to specified directory") do |path| 40 | # options[:output] = path 41 | #end 42 | opt.on("--source", "-s [FILE]", "data souce (YAML file)") do |file| 43 | options[:source] = file 44 | end 45 | opt.on("--tilt", "use Tilt for rendering instead of Malt") do 46 | options[:tilt] = true 47 | end 48 | opt.on("--trace", "show extra operational information") do 49 | $TRACE = true 50 | end 51 | opt.on("--dryrun", "-n", "don't actually write to disk") do 52 | $DRYRUN = true 53 | end 54 | opt.on("--debug", "run in debug mode") do 55 | $DEBUG = true 56 | $VERBOSE = true 57 | end 58 | opt.on_tail("--help", "display this help message") do 59 | puts opt 60 | exit 61 | end 62 | end 63 | 64 | end 65 | 66 | end 67 | -------------------------------------------------------------------------------- /work/trash/command.rb: -------------------------------------------------------------------------------- 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 | 85 | -------------------------------------------------------------------------------- /demo/01_overview.rdoc: -------------------------------------------------------------------------------- 1 | = Overview 2 | 3 | == Example Neapolitan Document 4 | 5 | Here is an example Neapolitan template, 'vanilla.np': 6 | 7 | output: vanilla.html 8 | 9 | --- erb rdoc 10 | 11 | = Yummy Vanilla 12 | 13 | Hi <%= name %>, 14 | 15 | I know you want some of that yummy stuff. 16 | 17 | --- coderay.ruby 18 | 19 | %{v a n i l l a}.each do |letter| 20 | puts "Give me a #{letter}!" 21 | end 22 | 23 | puts "What's that spell?" 24 | 25 | --- liquid html 26 | 27 | 28 | {{ yield }} 29 | 30 | 31 | --- textile 32 | 33 | | | 2009 | 2010 | 34 | | Has Vanilla? | No | Yes! | 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 | To load our example template, we can either pass a +File+ object to the 47 | +Template+ initializer. 48 | 49 | path = "vanilla.np" 50 | 51 | template = Neapolitan::Template.new(File.new(path)) 52 | 53 | Or we can use the shortcut +file+ method. 54 | 55 | template = Neapolitan.file(path) 56 | 57 | == Rendering Data Sources 58 | 59 | Neapolitan uses Malt on the backend. Malt supports a three separate ways to pass 60 | data into a template. 61 | 62 | The most obvious data source is a Hash. 63 | 64 | data = {:name=>"Tom"} 65 | 66 | text = template.render(data).to_s 67 | 68 | text.assert =~ /Hi Tom/ 69 | 70 | Templates can also be rendered given a Binding. 71 | 72 | name = "Huck" 73 | 74 | text = template.render(binding).to_s 75 | 76 | text.assert =~ /Hi Huck/ 77 | 78 | Lastly, they can be rendered with the scope of any other type of Object, 79 | including an instance of a Struct. 80 | 81 | scope = Struct.new(:name).new("Becky") 82 | 83 | text = template.render(scope).to_s 84 | 85 | text.assert =~ /Hi Becky/ 86 | 87 | -------------------------------------------------------------------------------- /work/trash/document.rb: -------------------------------------------------------------------------------- 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 | # :call-seq: 53 | # save(data={}, &block) 54 | # save(file, data={}, &block) 55 | # 56 | def save(*args, &block) 57 | data = Hash===args.last ? args.pop : {} 58 | path = args.first 59 | 60 | rendering = render(data, &block) 61 | 62 | path = path || rendering.header['file'] 63 | 64 | path = path || file.chomp(File.extname(file)) 65 | 66 | path = Dir.pwd unless path 67 | if File.directory?(path) 68 | file = File.join(path, file.chomp(File.extname(file)) + extension) 69 | else 70 | file = path 71 | end 72 | 73 | if $DRYRUN 74 | $stderr << "[DRYRUN] write #{fname}" 75 | else 76 | File.open(file, 'w'){ |f| f << rendering.to_s } 77 | end 78 | end 79 | 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /lib/neapolitan.rb: -------------------------------------------------------------------------------- 1 | module Neapolitan 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. 49 | # 50 | def self.load(source, options={}) 51 | Template.new(source, options) 52 | end 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. 61 | # 62 | def self.text(source, options={}) 63 | Template.new(source.to_s, options) 64 | end 65 | 66 | # Specifically create a new template from a file, given the files name. 67 | # 68 | # @example 69 | # Neapolitan::Template.file('example.np') 70 | # 71 | def self.file(fname, options={}) 72 | Template.new(File.new(fname), options) 73 | end 74 | 75 | end 76 | -------------------------------------------------------------------------------- /lib/neapolitan/part.rb: -------------------------------------------------------------------------------- 1 | module Neapolitan 2 | 3 | # A part is a section of a template. Templates can be segmented into 4 | # parts using the '--- FORMAT' notation. 5 | class Part 6 | 7 | # Parse text body and create new part. 8 | def self.parse(template, body) 9 | index = body.index("\n") 10 | format = body[0...index].strip 11 | text = body[index+1..-1].strip 12 | 13 | new(template, text, format) 14 | end 15 | 16 | # The template to which the part belongs. 17 | attr :template 18 | 19 | # Body of text as given in the part. 20 | attr :text 21 | 22 | # Setup new Part instance. 23 | # 24 | # @param [String] text 25 | # The parts body. 26 | # 27 | # @param [Array] formats 28 | # The template formats to apply to the body text. 29 | # 30 | def initialize(template, text, format) 31 | @template = template 32 | @text = text 33 | @format = format 34 | end 35 | 36 | # Rendering format as given in the template document. 37 | def format 38 | @format 39 | end 40 | 41 | # Part specific format split into array. 42 | def specific 43 | @_specific ||= split_format(format) 44 | end 45 | 46 | # Template default format split into array. 47 | def default 48 | @_default ||= split_format(template.default) 49 | end 50 | 51 | # Template default format split into array. 52 | def stencil 53 | @_stencil ||= split_format(template.stencil) 54 | end 55 | 56 | # Template default format split into array. 57 | def finish 58 | @_finish ||= split_format(template.finish) 59 | end 60 | 61 | # 62 | def formatting(&custom) 63 | if custom 64 | custom.call(self) 65 | else 66 | if specific.empty? 67 | stencil + default + finish 68 | else 69 | stencil + specific + finish 70 | end 71 | end 72 | end 73 | 74 | private 75 | 76 | # 77 | def split_format(format) 78 | case format 79 | when nil 80 | [] 81 | when Array 82 | format 83 | else 84 | format.to_str.split(/\s+/) 85 | end 86 | end 87 | end 88 | 89 | end 90 | -------------------------------------------------------------------------------- /work/byebye/site.rb: -------------------------------------------------------------------------------- 1 | require 'brite/config' 2 | require 'brite/page' 3 | require 'brite/post' 4 | require 'brite/layout' 5 | require 'brite/template' 6 | 7 | # 8 | module Brite 9 | 10 | # Site class 11 | class Site 12 | 13 | # Location of site. 14 | attr :location 15 | attr :output 16 | 17 | attr :layouts 18 | attr :pages 19 | attr :posts 20 | 21 | attr :dryrun 22 | attr :trace 23 | 24 | def initialize(options={}) 25 | @location = options[:location] || Dir.pwd 26 | @output = options[:output] || Dir.pwd 27 | @dryrun = options[:dryrun] 28 | @trace = options[:trace] 29 | 30 | @layouts = [] 31 | @pages = [] 32 | @posts = [] 33 | end 34 | 35 | def tags 36 | @tags ||= posts.map{ |p| p.tags }.flatten.uniq.sort 37 | end 38 | 39 | def posts_by_tag 40 | @posts_by_tag ||= ( 41 | chart ||= Hash.new{|h,k|h[k]=[]} 42 | posts.each do |post| 43 | post.tags.each do |tag| 44 | chart[tag] << post 45 | end 46 | end 47 | chart 48 | ) 49 | end 50 | 51 | # 52 | def trace? 53 | @trace 54 | end 55 | 56 | # DEPRECATE: replaced by trace? 57 | def verbose? 58 | @trace 59 | end 60 | 61 | # 62 | def build 63 | Dir.chdir(location) do 64 | sort_files 65 | if trace? 66 | puts "Layouts: " + layouts.join(", ") 67 | puts "Pages: " + pages.join(", ") 68 | puts "Posts: " + posts.join(", ") 69 | puts 70 | end 71 | render 72 | puts "#{pages.size + posts.size} Files: #{pages.size} Pages #{posts.size} Posts" 73 | end 74 | end 75 | 76 | def lookup_layout(name) 77 | layouts.find{ |l| name == l.name } 78 | end 79 | 80 | def sort_files 81 | files = Dir['**/*'] 82 | files.each do |file| 83 | temp = false 84 | name = File.basename(file) 85 | ext = File.extname(file) 86 | case ext 87 | when '.layout' 88 | layouts << Layout.new(self, file) 89 | when '.page' #*%w{.markdown .rdoc .textile .whtml} 90 | pages << Page.new(self, file) 91 | when '.post' 92 | posts << Post.new(self, file) 93 | end 94 | end 95 | posts.sort!{ |a,b| b.date <=> a.date } 96 | end 97 | 98 | def render 99 | render_posts # renger posts first, so pages can use them 100 | render_pages 101 | end 102 | 103 | def render_pages 104 | pages.each do |page| 105 | page.save(output) 106 | end 107 | end 108 | 109 | def render_posts 110 | posts.each do |post| 111 | post.save(output) 112 | end 113 | end 114 | 115 | def config 116 | @config ||= Config.new 117 | end 118 | 119 | def defaults 120 | config.defaults 121 | end 122 | 123 | def to_h 124 | pbt = {} 125 | posts_by_tag.each do |tag, posts| 126 | pbt[tag] = posts.map{ |p| p.to_h } 127 | end 128 | { 129 | 'posts' => posts.map{ |p| p.to_h }, 130 | 'posts_by_tag' => pbt, #posts_by_tag, #.map{ |t, ps| [t, ps.map{|p|p.to_h}] } 131 | 'tags' => tags 132 | } 133 | end 134 | 135 | def to_liquid 136 | to_h 137 | end 138 | 139 | end 140 | 141 | end 142 | -------------------------------------------------------------------------------- /lib/neapolitan/factory.rb: -------------------------------------------------------------------------------- 1 | module Neapolitan 2 | 3 | # Controls rendering to a variety of back-end templating 4 | # and markup systems via Malt. 5 | # 6 | class Factory 7 | # 8 | attr :types 9 | 10 | # @param [Hash] options 11 | # 12 | # @option options [Array] :types 13 | # 14 | def initialize(options={}) 15 | @types = options[:types] 16 | @system = options[:system] || :malt 17 | end 18 | 19 | # 20 | def render(text, format, scope, locals, &content) 21 | case format 22 | when /^coderay/ 23 | coderay(text, format) 24 | when /^syntax/ 25 | syntax(text, format) 26 | when /^rubypants/ 27 | rubypants(text, format) 28 | else 29 | if @system == :tilt 30 | render_via_tilt(text, format, scope, locals, &content) 31 | else 32 | render_via_malt(text, format, scope, locals, &content) 33 | end 34 | end 35 | end 36 | 37 | # Render via Malt. 38 | def render_via_malt(text, format, scope, locals, &content) 39 | doc = malt.text(text, :type=>format.to_sym) 40 | doc.render(scope, locals, &content) 41 | end 42 | 43 | # Render via Tilt. 44 | def render_via_tilt(text, format, scope, locals, &content) 45 | if engine = Tilt[format] 46 | #case data 47 | #when Hash 48 | # scope = Object.new 49 | # table = data 50 | #when Binding 51 | # scope = data.eval('self') 52 | # table = {} 53 | #else # object scope 54 | # scope = data 55 | # table = {} 56 | #end 57 | engine.new{text}.render(scope, locals, &content) 58 | else 59 | text 60 | end 61 | end 62 | 63 | # Get cached instance of Malt controller. 64 | # 65 | # @return [Malt::Machine] mutli-format renderer 66 | def malt 67 | @malt ||= ( 68 | if types && !types.empty? 69 | Malt::Machine.new(:types=>types) 70 | else 71 | Malt::Machine.new 72 | end 73 | ) 74 | end 75 | 76 | # Apply `coderay` syntax highlighting. This is a psuedo-format. 77 | def coderay(input, format) 78 | require 'coderay' 79 | format = format.split('.')[1] || :ruby #:plaintext 80 | tokens = CodeRay.scan(input, format.to_sym) #:ruby 81 | tokens.div() 82 | end 83 | 84 | # Apply `syntax` syntax highlighting. This is a psuedo-format. 85 | def syntax(input, format) 86 | require 'syntax/convertors/html' 87 | format = format.split('.')[1] || 'ruby' #:plaintext 88 | lines = true 89 | conv = Syntax::Convertors::HTML.for_syntax(format) 90 | conv.convert(input,lines) 91 | end 92 | 93 | # 94 | def rubypants(input_html, format) 95 | require 'rubypants' 96 | format, flag = format.split('.') 97 | flag = (flag || 2).to_i 98 | pants = RubyPants.new(input_html, flag) 99 | pantts.to_html 100 | end 101 | 102 | # 103 | #def render_stencil(stencil, text, attributes) 104 | # case stencil 105 | # when 'rhtml' 106 | # erb(text, attributes) 107 | # when 'liquid' 108 | # liquid(text, attributes) 109 | # else 110 | # text 111 | # end 112 | #end 113 | 114 | public 115 | 116 | # 117 | def self.render(text, format, data, &content) 118 | new.render(text, format, data, &content) 119 | end 120 | 121 | end 122 | 123 | end 124 | -------------------------------------------------------------------------------- /work/reference/page.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # "entia non sunt multiplicanda praeter necessitatem" 3 | # --Ockham's razor 4 | ## 5 | 6 | begin ; require 'rubygems' ; rescue LoadError ; end 7 | begin ; require 'erb' ; rescue LoadError ; end 8 | begin ; require 'redcloth' ; rescue LoadError ; end 9 | begin ; require 'bluecloth' ; rescue LoadError ; end 10 | 11 | begin 12 | require 'rdoc/markup/simple_markup' 13 | require 'rdoc/markup/simple_markup/to_html' 14 | rescue LoadError 15 | end 16 | 17 | # = Webrite module 18 | 19 | module Webrite 20 | 21 | # Page class renders a single page. 22 | 23 | class Page 24 | 25 | attr_reader :page 26 | 27 | attr_reader :main 28 | 29 | attr_reader :parts 30 | 31 | attr_reader :context 32 | 33 | # Accepts a raze file, which is simply a YAML map of parts, 34 | # that interface to the main part. 35 | 36 | #def self.load(file) 37 | # parts = YAML.load(File.read(file)) 38 | # parts = parts.inject({}) do |memo, (name, part)| 39 | # memo[name] = File.join(PARTS_DIR,part) 40 | # memo 41 | # end 42 | # new(parts) 43 | #end 44 | 45 | # Shiny New Raze Page. 46 | 47 | def initialize(page, parts={}) 48 | @page = page 49 | @parts = parts 50 | @main = parts.delete('main') #|| MAINS_ALT TODO Add a default section for pages.yaml 51 | 52 | raise "missing main section" unless @main 53 | 54 | @context = PageContext.new(self) #, parts) 55 | @binding = @context.bound #instance_eval{ binding } 56 | end 57 | 58 | # Render page to html. 59 | 60 | def to_html 61 | render_file(main) 62 | end 63 | 64 | def link_rel(path) 65 | path 66 | end 67 | 68 | # Render part. 69 | 70 | def render(name) 71 | file = @parts[name.to_s] 72 | raise "bad file -- #{name}" unless file 73 | render_file(file) 74 | end 75 | 76 | # Render file. 77 | 78 | def render_file(path) 79 | case File.extname(path) 80 | #when '.raze' 81 | # raze(path) 82 | when '.red' 83 | redcloth(path) 84 | when '.blue' 85 | redcloth(path) 86 | when '.rdoc' 87 | rdoc(path) 88 | when '.rhtml' 89 | eruby(path) 90 | when '.html' 91 | html(path) 92 | else 93 | raise "unknown file type" 94 | end 95 | end 96 | 97 | #def raze(path) 98 | # parts = YAML.load(File.read(path)) 99 | # Raze::Page.new(parts).to_html 100 | #end 101 | 102 | def redcloth(path) 103 | input = File.read(path) 104 | RedCloth.new(input).to_html 105 | end 106 | 107 | def bluecloth(path) 108 | input = File.read(path) 109 | BlueCloth.new(input).to_html 110 | end 111 | 112 | def rdoc(path) 113 | markup = SM::SimpleMarkup.new 114 | format = SM::ToHtml.new 115 | input = File.read(path) 116 | markup.convert(input, format) 117 | end 118 | 119 | def eruby(path) 120 | input = File.read(path) 121 | template = ERB.new(input) 122 | result = template.result(@binding) # (@binding) DOESN WORK, WHY? 123 | result 124 | end 125 | 126 | def html(path) 127 | File.read(path) 128 | end 129 | 130 | end 131 | 132 | # 133 | 134 | class PageContext 135 | 136 | def initialize(page) 137 | @page = page 138 | end 139 | 140 | # 141 | 142 | def render(name) 143 | @page.render(name) 144 | end 145 | 146 | # 147 | 148 | def render_file(path) 149 | @page.render_file(path) 150 | end 151 | 152 | # 153 | 154 | def part(name) 155 | @page.parts[name.to_s] 156 | end 157 | 158 | # 159 | 160 | def bound 161 | binding 162 | end 163 | 164 | end 165 | 166 | end 167 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | 5 | module DotRuby 6 | 7 | # 8 | class GemSpec 9 | 10 | # For which revision of .ruby is this gemspec intended? 11 | REVISION = 0 unless defined?(REVISION) 12 | 13 | # 14 | PATTERNS = { 15 | :bin_files => 'bin/*', 16 | :lib_files => 'lib/{**/}*.rb', 17 | :ext_files => 'ext/{**/}extconf.rb', 18 | :doc_files => '*.{txt,rdoc,md,markdown,tt,textile}', 19 | :test_files => '{test/{**/}*_test.rb,spec/{**/}*_spec.rb}' 20 | } unless defined?(PATTERNS) 21 | 22 | # 23 | def self.instance 24 | new.to_gemspec 25 | end 26 | 27 | attr :metadata 28 | 29 | attr :manifest 30 | 31 | # 32 | def initialize 33 | @metadata = YAML.load_file('.ruby') 34 | @manifest = Dir.glob('manifest{,.txt}', File::FNM_CASEFOLD).first 35 | 36 | if @metadata['revision'].to_i != REVISION 37 | warn "You have the wrong revision. Trying anyway..." 38 | end 39 | end 40 | 41 | # 42 | def scm 43 | @scm ||= \ 44 | case 45 | when File.directory?('.git') 46 | :git 47 | end 48 | end 49 | 50 | # 51 | def files 52 | @files ||= \ 53 | #glob_files[patterns[:files]] 54 | case 55 | when manifest 56 | File.readlines(manifest). 57 | map{ |line| line.strip }. 58 | reject{ |line| line.empty? || line[0,1] == '#' } 59 | when scm == :git 60 | `git ls-files -z`.split("\0") 61 | else 62 | Dir.glob('{**/}{.*,*}') # TODO: be more specific using standard locations ? 63 | end.select{ |path| File.file?(path) } 64 | end 65 | 66 | # 67 | def glob_files(pattern) 68 | Dir.glob(pattern).select { |path| 69 | File.file?(path) && files.include?(path) 70 | } 71 | end 72 | 73 | # 74 | def patterns 75 | PATTERNS 76 | end 77 | 78 | # 79 | def executables 80 | @executables ||= \ 81 | glob_files(patterns[:bin_files]).map do |path| 82 | File.basename(path) 83 | end 84 | end 85 | 86 | def extensions 87 | @extensions ||= \ 88 | glob_files(patterns[:ext_files]).map do |path| 89 | File.basename(path) 90 | end 91 | end 92 | 93 | # 94 | def name 95 | metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_') 96 | end 97 | 98 | # 99 | def to_gemspec 100 | Gem::Specification.new do |gemspec| 101 | gemspec.name = name 102 | gemspec.version = metadata['version'] 103 | gemspec.summary = metadata['summary'] 104 | gemspec.description = metadata['description'] 105 | 106 | metadata['authors'].each do |author| 107 | gemspec.authors << author['name'] 108 | 109 | if author.has_key?('email') 110 | if gemspec.email 111 | gemspec.email << author['email'] 112 | else 113 | gemspec.email = [author['email']] 114 | end 115 | end 116 | end 117 | 118 | gemspec.licenses = metadata['copyrights'].map{ |c| c['license'] }.compact 119 | 120 | metadata['requirements'].each do |req| 121 | name = req['name'] 122 | version = req['version'] 123 | groups = req['groups'] || [] 124 | 125 | case version 126 | when /^(.*?)\+$/ 127 | version = ">= #{$1}" 128 | when /^(.*?)\-$/ 129 | version = "< #{$1}" 130 | when /^(.*?)\~$/ 131 | version = "~> #{$1}" 132 | end 133 | 134 | if groups.empty? or groups.include?('runtime') 135 | # populate runtime dependencies 136 | if gemspec.respond_to?(:add_runtime_dependency) 137 | gemspec.add_runtime_dependency(name,*version) 138 | else 139 | gemspec.add_dependency(name,*version) 140 | end 141 | else 142 | # populate development dependencies 143 | if gemspec.respond_to?(:add_development_dependency) 144 | gemspec.add_development_dependency(name,*version) 145 | else 146 | gemspec.add_dependency(name,*version) 147 | end 148 | end 149 | end 150 | 151 | # convert external dependencies into a requirements 152 | if metadata['external_dependencies'] 153 | ##gemspec.requirements = [] unless metadata['external_dependencies'].empty? 154 | metadata['external_dependencies'].each do |req| 155 | gemspec.requirements << req.to_s 156 | end 157 | end 158 | 159 | # determine homepage from resources 160 | homepage = metadata['resources'].find{ |key, url| key =~ /^home/ } 161 | gemspec.homepage = homepage.last if homepage 162 | 163 | gemspec.require_paths = metadata['load_path'] || ['lib'] 164 | gemspec.post_install_message = metadata['install_message'] 165 | 166 | # RubyGems specific metadata 167 | gemspec.files = files 168 | gemspec.extensions = extensions 169 | gemspec.executables = executables 170 | 171 | if Gem::VERSION < '1.7.' 172 | gemspec.default_executable = gemspec.executables.first 173 | end 174 | 175 | gemspec.test_files = glob_files(patterns[:test_files]) 176 | 177 | unless gemspec.files.include?('.document') 178 | gemspec.extra_rdoc_files = glob_files(patterns[:doc_files]) 179 | end 180 | end 181 | end 182 | 183 | end #class GemSpec 184 | 185 | end 186 | 187 | DotRuby::GemSpec.instance 188 | -------------------------------------------------------------------------------- /work/trash/template.rb: -------------------------------------------------------------------------------- 1 | require 'neapolitan/factory' 2 | require 'neapolitan/part' 3 | 4 | module Neapolitan 5 | 6 | # 7 | class Template 8 | 9 | # Template text. 10 | attr :text 11 | 12 | # File name of template, if given. 13 | attr :file 14 | 15 | # Header data, also known as front matter. 16 | attr :header 17 | 18 | # Templating format to apply "whole-clothe". 19 | attr :stencil 20 | 21 | # Default format(s) for undecorated parts. 22 | # If not otherwise set the default is 'html'. 23 | attr :default 24 | 25 | # Common format(s) to be applied to all parts. 26 | # These are applied at the end. 27 | attr :common 28 | 29 | # Rendering formats to disallow. 30 | attr :reject 31 | 32 | # 33 | def initialize(source, options={}) 34 | case source 35 | when ::File 36 | @file = source.path #name 37 | @text = source.read 38 | source.close 39 | when ::IO 40 | @text = source.read 41 | @file = options[:file] 42 | source.close 43 | when ::String 44 | @text = source 45 | @file = options[:file] 46 | when Hash 47 | options = source 48 | source = nil 49 | @file = options[:file] 50 | @text = File.read(@file) 51 | end 52 | 53 | @stencil = options[:stencil] 54 | @default = options[:default] || 'html' 55 | @common = options[:common] 56 | 57 | @types = options[:types] 58 | 59 | parse 60 | end 61 | 62 | # 63 | def inspect 64 | if file 65 | "<#{self.class}: @file='#{file}'>" 66 | else 67 | "<#{self.class}: @text='#{text[0,10]}'>" 68 | end 69 | end 70 | 71 | # Rejection procedure. 72 | def reject(&block) 73 | @reject = block if block 74 | @reject 75 | end 76 | 77 | # Unrendered template parts. 78 | def parts 79 | @parts 80 | end 81 | 82 | # 83 | def render(data={}, &block) 84 | Rendering.new(self, data, &block) 85 | end 86 | 87 | # :call-seq: 88 | # save(data={}, &block) 89 | # save(file, data={}, &block) 90 | # 91 | def save(*args, &block) 92 | data = Hash===args.last ? args.pop : {} 93 | path = args.first 94 | 95 | rendering = render(data, &block) 96 | 97 | path = path || rendering.header['file'] 98 | 99 | path = path || file.chomp(File.extname(file)) 100 | 101 | path = Dir.pwd unless path 102 | if File.directory?(path) 103 | file = File.join(path, file.chomp(File.extname(file)) + extension) 104 | else 105 | file = path 106 | end 107 | 108 | if $DRYRUN 109 | $stderr << "[DRYRUN] write #{fname}" 110 | else 111 | File.open(file, 'w'){ |f| f << rendering.to_s } 112 | end 113 | end 114 | 115 | private 116 | 117 | # TODO: Should a stencil be applied once to the entire document? 118 | def parse 119 | @parts = [] 120 | 121 | sect = text.split(/^\-\-\-/) 122 | 123 | if sect.size == 1 124 | @header = {} 125 | @parts << Part.new(sect[0]) #, *[@stencil, @default].compact.flatten) 126 | else 127 | sect.shift if sect.first.strip.empty? 128 | 129 | head = sect.shift 130 | head = YAML::load(head) 131 | parse_header(head) 132 | 133 | sect.each do |body| 134 | index = body.index("\n") 135 | formats = body[0...index].strip 136 | formats = formats.split(/\s+/) if String===formats 137 | #formats = @default if formats.empty? 138 | #formats.unshift(@stencil) if @stencil 139 | text = body[index+1..-1] 140 | @parts << Part.new(text, *formats) 141 | end 142 | end 143 | end 144 | 145 | # 146 | def parse_header(head) 147 | @header = head 148 | @default = head.delete('default'){ @default } 149 | @common = head.delete('common'){ @common } 150 | end 151 | 152 | end 153 | 154 | # Template Rendering 155 | class Rendering 156 | 157 | # 158 | attr :template 159 | 160 | # Source of data, can either be an 161 | # Hash, Binding or Object. 162 | attr :data 163 | 164 | # 165 | attr :block 166 | 167 | # 168 | def initialize(template, data, &block) 169 | @template = template 170 | @data = data 171 | @block = block 172 | 173 | if !block 174 | case data 175 | when Hash 176 | yld = data.delete('yield') 177 | @block = Proc.new{ yld } if yld 178 | end 179 | @block = Proc.new{''} unless @block 180 | end 181 | 182 | render 183 | end 184 | 185 | def to_s 186 | #render unless @output 187 | @output 188 | end 189 | 190 | # Renderings of each part. 191 | def to_a 192 | #render unless @output 193 | @renders 194 | end 195 | 196 | # Summary is the rendering of the first part. 197 | def summary 198 | #render unless @output 199 | @summary 200 | end 201 | 202 | # 203 | def header 204 | @template.header 205 | end 206 | 207 | private 208 | 209 | def render 210 | @renders = @template.parts.map{ |part| render_part(part, @data, &@block) } 211 | @summary = @renders.first 212 | @output = @renders.join("\n") 213 | end 214 | 215 | # 216 | def render_part(part, data, &block) 217 | formats = part.formats 218 | formats = template.default if formats.empty? 219 | formats = [formats, template.common].flatten.compact 220 | 221 | case template.reject 222 | when Array 223 | formats = formats - template.reject 224 | when Proc 225 | formats = formats.reject(&template.reject) 226 | end 227 | 228 | formats = [template.stencil, *formats].compact 229 | 230 | formats.inject(part.text) do |text, format| 231 | factory.render(text, format, data, &block) 232 | end 233 | end 234 | 235 | # 236 | def factory 237 | @factory ||= Factory.new 238 | end 239 | 240 | end 241 | 242 | end 243 | -------------------------------------------------------------------------------- /work/trash/factory.rb: -------------------------------------------------------------------------------- 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 via Malt. 8 | # 9 | class Factory 10 | 11 | # 12 | attr :types 13 | 14 | # 15 | def initialize(options={}) 16 | @types = options[:types] 17 | end 18 | 19 | # 20 | def render(text, format, data, &yld) 21 | case format 22 | when /^coderay/ 23 | coderay(text, format) 24 | when /^syntax/ 25 | syntax(text, format) 26 | else 27 | render_via_malt(text, format, data, &yld) 28 | end 29 | end 30 | 31 | # 32 | def render_via_malt(text, format, data, &yld) 33 | doc = malt.text(text, :type=>format.to_sym) 34 | doc.render(data, &yld) 35 | end 36 | 37 | # 38 | def render_via_tilt(text, format, data, &yld) 39 | if engine = Tilt[format] 40 | case data 41 | when Hash 42 | scope = Object.new 43 | table = data 44 | when Binding 45 | scope = data.eval('self') 46 | table = {} 47 | else # object scope 48 | scope = data 49 | table = {} 50 | end 51 | engine.new{text}.render(scope, table, &yld) 52 | else 53 | text 54 | end 55 | end 56 | 57 | # 58 | def malt 59 | @malt ||= ( 60 | if types && !types.empty? 61 | Malt::Machine.new(:types=>types) 62 | else 63 | Malt::Machine.new 64 | end 65 | ) 66 | end 67 | 68 | # 69 | #def redcloth(input) 70 | # RedCloth.new(input).to_html 71 | #end 72 | 73 | #def bluecloth(input) 74 | # BlueCloth.new(input).to_html 75 | #end 76 | 77 | #def rdiscount(input) 78 | # RDiscount.new(input).to_html 79 | #end 80 | 81 | def rdoc(input) 82 | markup = RDoc::Markup::ToHtml.new 83 | markup.convert(input) 84 | end 85 | 86 | #def haml(input) 87 | # Haml::Engine.new(input).render 88 | #end 89 | 90 | def coderay(input, format) 91 | require 'coderay' 92 | format = format.split('.')[1] || :ruby #:plaintext 93 | tokens = CodeRay.scan(input, format.to_sym) #:ruby 94 | tokens.div() 95 | end 96 | 97 | # 98 | def syntax(input, format) 99 | require 'syntax/convertors/html' 100 | format = format.split('.')[1] || 'ruby' #:plaintext 101 | lines = true 102 | conv = Syntax::Convertors::HTML.for_syntax(format) 103 | conv.convert(input,lines) 104 | end 105 | 106 | # Stencil Rendering 107 | # ----------------- 108 | 109 | # 110 | #def render_stencil(stencil, text, attributes) 111 | # case stencil 112 | # when 'rhtml' 113 | # erb(text, attributes) 114 | # when 'liquid' 115 | # liquid(text, attributes) 116 | # else 117 | # text 118 | # end 119 | #end 120 | 121 | # 122 | #def erb(input, attributes) 123 | # template = ERB.new(input) 124 | # context = TemplateContext.new(attributes) 125 | # result = template.result(context.__binding__) 126 | # result 127 | #end 128 | 129 | #def liquid(input, attributes) 130 | # template = Liquid::Template.parse(input) 131 | # result = template.render(attributes, :filters => [TemplateFilters]) 132 | # result 133 | #end 134 | 135 | =begin 136 | # Require Dependencies 137 | # -------------------- 138 | 139 | # TODO: Load engines only if used. 140 | 141 | begin ; require 'rubygems' ; rescue LoadError ; end 142 | begin ; require 'erb' ; rescue LoadError ; end 143 | begin ; require 'redcloth' ; rescue LoadError ; end 144 | begin ; require 'bluecloth' ; rescue LoadError ; end 145 | begin ; require 'rdiscount' ; rescue LoadError ; end 146 | 147 | begin 148 | require 'liquid' 149 | #Liquid::Template.register_filter(TemplateFilters) 150 | rescue LoadError 151 | end 152 | 153 | begin 154 | require 'haml' 155 | #Haml::Template.options[:format] = :html5 156 | rescue LoadError 157 | end 158 | =end 159 | 160 | private 161 | 162 | def require_rdoc 163 | begin 164 | require 'rdoc/markup' 165 | require 'rdoc/markup/to_html' 166 | rescue LoadError 167 | end 168 | end 169 | 170 | public 171 | 172 | # 173 | def self.render(text, format, data, &yld) 174 | new.render(text, format, data, &yld) 175 | end 176 | 177 | end 178 | 179 | 180 | =begin 181 | # = Clean Rendering Context 182 | # 183 | # The Factory Context is is used by ERB. 184 | 185 | class Context 186 | #include TemplateFilters 187 | 188 | instance_methods(true).each{ |m| private m unless m =~ /^__/ } 189 | 190 | def initialize(attributes={}) 191 | @attributes = attributes 192 | end 193 | 194 | def __binding__ 195 | binding 196 | end 197 | 198 | def to_h 199 | @attributes 200 | end 201 | 202 | def method_missing(s, *a) 203 | s = s.to_s 204 | @attributes.key?(s) ? @attributes[s] : super 205 | end 206 | end 207 | =end 208 | 209 | # 210 | # 211 | # 212 | 213 | #module TemplateFilters 214 | 215 | # NOTE: HTML truncate did not work well. 216 | 217 | # # HTML comment regular expression 218 | # REM_RE = %r{<\!--(.*?)-->} 219 | # 220 | # # HTML tag regular expression 221 | # TAG_RE = %r{\s]+))?)+\s*|\s*)/?>} #' 222 | # 223 | # # 224 | # def truncate_html(html, limit) 225 | # return html unless limit 226 | # 227 | # mask = html.gsub(REM_RE){ |m| "\0" * m.size } 228 | # mask = mask.gsub(TAG_RE){ |m| "\0" * m.size } 229 | # 230 | # i, x = 0, 0 231 | # 232 | # while i < mask.size && x < limit 233 | # x += 1 if mask[i] != "\0" 234 | # i += 1 235 | # end 236 | # 237 | # while x > 0 && mask[x,1] == "\0" 238 | # x -= 1 239 | # end 240 | # 241 | # return html[0..x] 242 | # end 243 | 244 | #end 245 | 246 | end 247 | 248 | -------------------------------------------------------------------------------- /work/reference/server.rb: -------------------------------------------------------------------------------- 1 | # TITLE: 2 | # 3 | # Raze Server 4 | # 5 | # SUMMARY: 6 | # 7 | # The Raze server uses Rack to provide a simple means of serving 8 | # a Raze-based site. One can use this as an alternative to 9 | # generating the site, or simply for testing purposes before 10 | # deploying. 11 | 12 | require 'webrite/page' 13 | 14 | begin 15 | require 'rack' 16 | require 'rack/request' 17 | require 'rack/response' 18 | rescue LoadError => e 19 | if require 'rubygems' 20 | retry 21 | else 22 | raise e 23 | end 24 | end 25 | 26 | 27 | module Webrite 28 | 29 | # TODO Make alterable? 30 | 31 | def self.root 32 | Dir.pwd 33 | end 34 | 35 | # = Raze Rack-based Server 36 | # 37 | # The server looks for a configuration file 'server.yaml' in your raze site's 38 | # root directory. These are configuration options fed to Rack's handler. Use 39 | # 'adapter' entry in this to select which handler to use (webrick, cgi, fcgi). 40 | # If not server.yaml file is found, or entries not given, the deafult settings 41 | # are Webrick on port 8181. 42 | 43 | class Server 44 | 45 | DEFAULT_ROUTE = "routes.yaml" 46 | 47 | DEFAULT_CONFIG = { 'port' => 8181 } 48 | 49 | def self.adapters(name) 50 | case name..to_s.downcase 51 | when 'webrick' 52 | Rack::Handler::WEBrick 53 | when 'cgi' 54 | Rack::Handler::CGI 55 | when 'fcgi' 56 | Rack::Handler::FastCGI 57 | else 58 | Rack::Handler::WEBrick 59 | end 60 | end 61 | 62 | # Go Raze! 63 | 64 | def self.start 65 | config = load_config 66 | adapter = config.delete(:Adapter) 67 | #use Rack::ShowExceptions 68 | server = Raze::Server.new 69 | 70 | adapters(adapter).run(server, config) 71 | end 72 | 73 | # Load server configuration. 74 | 75 | def self.load_config 76 | default = DEFAULT_CONFIG 77 | if File.file?('server.yaml') 78 | default.update(YAML.load(File.new("server.yaml"))) 79 | end 80 | default.inject({}){|m, (k,v)| m[k.capitalize.to_sym] = v; m} 81 | end 82 | 83 | # Shiny new Raze site server. 84 | 85 | def initialize 86 | #load_config 87 | load_routes 88 | end 89 | 90 | # Load routes. 91 | 92 | def load_routes 93 | @routes = [] 94 | routes = YAML.load(DEFAULT_ROUTE) 95 | routes.each do |target, parts| 96 | puts "[Add Route] " + target 97 | #name = name.chomp(File.extname(name)) 98 | load_route(target, parts) 99 | end 100 | end 101 | 102 | # Load a route. 103 | 104 | def load_route(target, parts) 105 | @routes << Route.new(target, parts) 106 | end 107 | 108 | # for rack 109 | 110 | def call(env) 111 | req = Request.new( 112 | :method => env['REQUEST_METHOD'], # GET/POST 113 | :script => env['SCRIPT_NAME'], # The initial portion of the request URL’s "path" that corresponds to the application object, so that the application knows its virtual "location". This may be an empty string, if the application corresponds to the "root" of the server. 114 | :path => env['PATH_INFO'], # The remainder of the request URL’s "path", designating the virtual "location" of the request’s target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash. 115 | :query => env['QUERY_STRING'], # The portion of the request URL that follows the ?, if any. May be empty, but is always required! 116 | :domain => env['SERVER_NAME'], # When combined with SCRIPT_NAME and PATH_INFO, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. 117 | :port => env['SERVER_PORT'] 118 | #env['HTTP_???'] 119 | ) 120 | return respond(req) 121 | end 122 | 123 | # Respond to request. 124 | 125 | def respond(request) 126 | puts "[Request] " + request.path 127 | 128 | route = find_route(request.path) 129 | if route 130 | status, header, body = route.respond(request) 131 | else 132 | path = request.path 133 | path = path[1..-1] if path[0,1] == '/' 134 | if File.exist?(path) # pass through to webserver? 135 | status = 200 136 | header = {"Content-Type" => "text/plain"} # how to decipher? 137 | body = File.new(path) 138 | else 139 | status = 404 140 | header = {"Content-Type" => "text/html"} 141 | body = "

404

" # FIX just return rack exception page if development mode. 142 | end 143 | end 144 | 145 | return status, header, body 146 | end 147 | 148 | # Find a route for the given url. 149 | 150 | def find_route(url) 151 | @routes.each do |route| 152 | if route.match(url) 153 | return route 154 | end 155 | end 156 | nil 157 | end 158 | 159 | end 160 | 161 | 162 | # Site Request 163 | 164 | class Request 165 | attr_accessor :method 166 | attr_accessor :script 167 | attr_accessor :path 168 | attr_accessor :query 169 | attr_accessor :domain 170 | attr_accessor :port 171 | 172 | def initialize(options) 173 | options.each do |k,v| send("#{k}=", v) end 174 | end 175 | end 176 | 177 | 178 | # Site Route 179 | 180 | class Route 181 | attr :route 182 | attr :parts 183 | 184 | #def self.load(file) 185 | # new(YAML.load(File.new(file))) 186 | #end 187 | 188 | def initialize(route, parts) 189 | @route = route #'/' + File.basename(file).chomp('.raze') 190 | @parts = parts 191 | end 192 | 193 | def match(url) 194 | route == url || route + 'html' == url 195 | #Regexp.new(@route).match(url) 196 | end 197 | 198 | def respond(request) 199 | body = Page.new(route, parts).to_html 200 | return 200, {"Content-Type" => "text/html"}, [body] 201 | end 202 | 203 | end 204 | 205 | end 206 | -------------------------------------------------------------------------------- /work/reference/generator.rb: -------------------------------------------------------------------------------- 1 | require 'webrite/page' 2 | require 'fileutils' 3 | 4 | # = Webrite module 5 | 6 | module Webrite 7 | 8 | # Site generator 9 | 10 | class Generator 11 | 12 | class NoSource < ArgumentError 13 | def message; "No source folder."; end 14 | end 15 | 16 | #SOURCE = 'parts' 17 | SITEMAP = 'sitemap.yaml' 18 | 19 | RSYNC_FILTER = %{ 20 | - .svn 21 | P wiki 22 | P robot.txt 23 | P statcvs 24 | P statsvn 25 | P usage 26 | }.gsub(/^\s*/m,'') 27 | 28 | # New Generator. 29 | 30 | def initialize(options={}) 31 | #@source = options[:source] || SOURCE 32 | @sitemap = options[:sitemap] || SITEMAP 33 | @output = options[:output] || '.' 34 | 35 | @noharm = options[:noharm] || options[:dryrun] 36 | @trace = options[:trace] 37 | 38 | #unless File.directory?(@source) 39 | # raise(NoSource, "no parts source -- #{@source}") 40 | #end 41 | 42 | unless File.file?(@sitemap) 43 | raise(LoadError, "sitemap file not found -- #{@sitemap}") 44 | end 45 | 46 | # link vs install? 47 | end 48 | 49 | def noharm? ; @noharm ; end 50 | alias_method :dryrun?, :noharm? 51 | 52 | # Load routes. 53 | 54 | def routes 55 | @routes ||= ( 56 | YAML::load(File.open(@sitemap)) 57 | ) 58 | end 59 | 60 | # Generate site. 61 | 62 | def generate 63 | actions = routes.collect do |page, parts| 64 | case page 65 | when '.rsync-filter' 66 | ['rsync', page, parts] 67 | else 68 | case parts 69 | #when String 70 | # ['ditto', page, parts] 71 | when Hash 72 | ['page', page, parts] 73 | when YAML::PrivateType 74 | [parts.type_id, page, parts.value] 75 | else 76 | raise "Unknown target type." 77 | end 78 | end 79 | end 80 | 81 | actions.each do |action, page, parts| 82 | send("handle_#{action}", page, parts) 83 | end 84 | end 85 | 86 | # # Link handler. 87 | # # This is a lighter alternative to using install. 88 | # 89 | # def handle_link(target, parts) 90 | # part, glob = parts.split(/\s+/) 91 | # if File.directory?(File.join(@source, part)) 92 | # files = nil 93 | # Dir.chdir(File.join(@source, part)) do 94 | # files = Dir.glob(glob||'**/*') 95 | # files = files.select{|file| File.file?(file)} 96 | # end 97 | # files.each do |file| 98 | # src = File.join(@source, part, file) 99 | # dst = File.join(@output, target, file) 100 | # link(src, dst) 101 | # end 102 | # else 103 | # src = File.join(@source, part) 104 | # dst = File.join(@output, target) 105 | # link(src,dst) 106 | # end 107 | # end 108 | 109 | # # Rule handler. 110 | # 111 | # def handle_rule(page, parts) 112 | # i = 0 113 | # page = page.gsub('*'){ |m| '\\' + "#{i += 1}" } 114 | # parts = parts.gsub('*', '(.*?)') 115 | # regex = Regexp.new(parts) 116 | # files = source_files 117 | # 118 | # copy = [] 119 | # Dir.chdir(@source) do 120 | # files.grep(regex){ |file| 121 | # next unless File.file?(file) 122 | # dest = file.sub(regex, page) 123 | # copy << [file, dest] 124 | # } 125 | # end 126 | # 127 | # copy.each do |src, dst| 128 | # src = File.join(@source, src) 129 | # dst = File.join(@output, dst) 130 | # link(src, dst) 131 | # #unless File.exist?(dest) 132 | # # fs.mkdir_p(File.dirname(dest)) 133 | # # fs.ln(srce, dest) 134 | # # puts " " + dest 135 | # #end 136 | # end 137 | # end 138 | 139 | # Handle rysnc filter. 140 | 141 | def handle_rsync(page, parts) 142 | case parts 143 | when String 144 | parts = parts.strip + "\n" 145 | parts << "- #{@source}" 146 | parts << "- #{@sitemap}" 147 | parts << RSYNC_FILTER 148 | end 149 | Dir.chdir(@output) do 150 | File.open('.rsync-filter', 'w'){ |f| f << parts } 151 | end 152 | end 153 | 154 | # Page handler. 155 | 156 | def handle_page(page, parts) 157 | dest = File.join(@output, page) 158 | time = last_modified(parts.values) 159 | 160 | return if File.exist?(dest) && time < File.mtime(dest) 161 | 162 | html = Page.new(page, parts).to_html 163 | 164 | if noharm? 165 | puts "#{$0} #{dest}" 166 | else 167 | fs.mkdir_p(File.dirname(dest)) 168 | File.open(dest,'w'){ |f| f << html } 169 | puts dest unless @silent 170 | end 171 | end 172 | 173 | 174 | 175 | # # 176 | # 177 | # def source_files(glob=nil) 178 | # glob ||= '**/*' 179 | # @source_files ||= {} 180 | # @source_files[glob] ||= ( 181 | # files = nil 182 | # Dir.chdir(@source) do 183 | # files = Dir.glob(glob) 184 | # end 185 | # files 186 | # ) 187 | # end 188 | 189 | # 190 | 191 | def fs 192 | @noharm ? FileUtils::DryRun : FileUtils 193 | end 194 | 195 | # 196 | 197 | # def link(src, dst) 198 | # unless File.exist?(dst) && File.mtime(dst) >= File.mtime(src) 199 | # fs.mkdir_p(File.dirname(dst)) 200 | # fs.rm(dst) if File.exist?(dst) 201 | # fs.ln(src, dst) 202 | # puts dst unless @silent 203 | # end 204 | # end 205 | 206 | # Returns the most recent modified time from the 207 | # list of source files. 208 | 209 | def last_modified(parts) 210 | files = parts.select{ |f| File.file?(f) } 211 | times = files.collect do |part| 212 | file = File.join(part) 213 | File.mtime(file) 214 | end 215 | times.max 216 | end 217 | 218 | end 219 | end 220 | 221 | 222 | 223 | 224 | # # Straight install copy. 225 | # # 226 | # # example: !!install 227 | # # source: examples/**/* 228 | # # mode: 0444 229 | # 230 | # def handle_install(target, parts) 231 | # if Hash===parts 232 | # glob = parts['source'] 233 | # mode = parts['mode'] 234 | # else 235 | # glob = parts 236 | # end 237 | # files = source_files(glob) 238 | # files.each do |file| 239 | # src = File.join(@source, file) 240 | # dst = File.join(target, file) 241 | # fs.install(src, dst) 242 | # end 243 | # end 244 | -------------------------------------------------------------------------------- /lib/neapolitan/template.rb: -------------------------------------------------------------------------------- 1 | module Neapolitan 2 | 3 | # Template class is the main interface class. 4 | # 5 | class Template 6 | 7 | # Backend templating system used. Either `malt` or `tilt`. 8 | # 9 | # Note that Neapolitan is designed with Malt in mind, but Tilt 10 | # should work fine in most cases too. Keep in mind that Tilt 11 | # uses `yield` to render block content, where as Malt uses `content`. 12 | attr :system 13 | 14 | # Template text. 15 | attr :text 16 | 17 | # File name of template, if given. 18 | attr :file 19 | 20 | # Default format(s) for undecorated parts. If not otherwise set the 21 | # part is rendered exactly as given (which usually means `html`). 22 | attr :default 23 | 24 | # Template format(s) to apply to all sections. Typically this 25 | # will be a polyglot template format that injects data and performs 26 | # conditional rendering, such as `erb` or `liquid`. But it can 27 | # contain multiple formats. 28 | # 29 | # @example 30 | # stencil 'erb' 31 | # 32 | attr :stencil 33 | 34 | # Post-formatting to be applied to all parts. These formats are applied 35 | # after the part specific formats. This can be useful for post-formatting 36 | # such as `rubypants`, a SmartyPants formatter. 37 | # 38 | attr :finish 39 | 40 | # Data provided in template header. Also known in some circles as 41 | # YAML front-matter. 42 | attr :metadata 43 | 44 | # @deprecated 45 | alias_method :header, :metadata 46 | 47 | # Unrendered template parts. 48 | attr :parts 49 | 50 | # 51 | def initialize(source, options={}) 52 | case source 53 | when ::File 54 | @file = source.path #name 55 | @text = source.read 56 | source.close 57 | when ::IO 58 | @text = source.read 59 | @file = options[:file] 60 | source.close 61 | when ::String 62 | @text = source 63 | @file = options[:file] 64 | when Hash 65 | options = source 66 | source = nil 67 | @file = options[:file] 68 | @text = File.read(@file) 69 | end 70 | 71 | @select = Neapolitan.select 72 | @reject = Neapolitan.reject 73 | 74 | @system = options[:system] || Neapolitan.system || 'malt' 75 | 76 | require @system.to_s 77 | 78 | @default = options[:default] || 'html' # FIXME: 'text' 79 | @stencil = options[:stencil] 80 | @finish = options[:finish] 81 | 82 | parse 83 | end 84 | 85 | # 86 | def inspect 87 | if file 88 | "<#{self.class}: @file='#{file}'>" 89 | else 90 | "<#{self.class}: @text='#{text[0,10]}...'>" 91 | end 92 | end 93 | 94 | # Apply complex formating rules to parts. 95 | # 96 | # Here is an example of how formatting is determined 97 | # when no formatting block is given. 98 | # 99 | # template.format do |part| 100 | # if part.specific.empty? 101 | # part.stencil + part.default + part.finish 102 | # else 103 | # part.stencil + part.specific + part.finish 104 | # end 105 | # end 106 | # 107 | def format(&block) 108 | @format = block if block 109 | @format 110 | end 111 | 112 | # TODO: filter common and default 113 | 114 | # Reject formats, limiting the template to only the remaining supported 115 | # formats. 116 | def reject(&block) 117 | @reject = block if block 118 | @reject 119 | end 120 | 121 | # TODO: filter common and default 122 | 123 | # Select formats, limit the template to only the specified formats. 124 | def select(&block) 125 | @select = block if block 126 | @select 127 | end 128 | 129 | # Render document. 130 | # 131 | # @return [Rendering] 132 | # The encapsulation of templates completed rendering. 133 | # 134 | def render(data={}, &content) 135 | 136 | # TODO: is this content block buiness here needed any more? 137 | #if !content 138 | # case data 139 | # when Hash 140 | # yld = data.delete('yield') 141 | # content = Proc.new{ yld } if yld 142 | # end 143 | # content = Proc.new{''} unless content 144 | #end 145 | 146 | # apply stencil whole-clothe 147 | #body = apply_stencil(@body, scope, locals, &content) 148 | 149 | #parts = parse_parts(body) 150 | 151 | case data 152 | when Hash 153 | scope = Object.new 154 | locals = @metadata.merge(data.rekey) 155 | else 156 | scope = data 157 | locals = @metadata 158 | end 159 | 160 | rendered_parts = parts.map{ |part| render_part(part, scope, locals, &content) } 161 | 162 | Rendering.new(rendered_parts, @metadata) 163 | end 164 | 165 | # Save template to disk. 166 | # 167 | # @overload save(data={}, &content) 168 | # Name of file is the same as the given template 169 | # file less it's extension. 170 | # 171 | # @param [Hash] data 172 | # 173 | # @return nothing 174 | # 175 | # @overload save(file, data={}, &content) 176 | # 177 | # @param [String] file to save as 178 | # 179 | # @param [Hash] data 180 | # 181 | # @return nothing 182 | def save(*args, &content) 183 | data = Hash===args.last ? args.pop : {} 184 | path = args.first 185 | 186 | rendering = render(data, &content) 187 | 188 | path = path || rendering.metadata['output'] 189 | path = path || path.chomp(File.extname(file)) 190 | 191 | path = Dir.pwd unless path 192 | if File.directory?(path) 193 | file = File.join(path, file.chomp(File.extname(file)) + extension) 194 | else 195 | file = path 196 | end 197 | 198 | if $DRYRUN 199 | $stderr << "[DRYRUN] write #{fname}" 200 | else 201 | File.open(file, 'w'){ |f| f << rendering.to_s } 202 | end 203 | end 204 | 205 | private 206 | 207 | # TODO: Should a stencil be applied once to the entire document? 208 | # While it would be nice, b/c it would speed things up a bit, it 209 | # could present an issue with the `---` dividers and would be useless 210 | # for certain formats like Haml. So probably not. 211 | 212 | # Apply stencil whole-clothe. 213 | #def apply_stencil(body, scope, locals, &content) 214 | # return body unless stencil 215 | # factory.render(body, stencil, scope, locals, &content) 216 | #end 217 | 218 | # Parse template document into metadata and parts. 219 | def parse 220 | parts = text.split(/^\-\-\-/) 221 | 222 | if parts.size == 1 223 | data = {} 224 | #@parts << Part.new(sect[0]) #, *[@stencil, @default].compact.flatten) 225 | else 226 | parts.shift if parts.first.strip.empty? 227 | data = YAML::load(parts.first) 228 | if Hash === data 229 | parts.shift 230 | else 231 | data = {} 232 | end 233 | end 234 | 235 | parse_metadata(data) 236 | 237 | @parts = parts.map{ |part| Part.parse(self, part) } 238 | end 239 | 240 | # 241 | def parse_metadata(data) 242 | @default = data.delete('default') if data.key?('default') 243 | @stencil = data.delete('stencil') if data.key?('stencil') 244 | @finish = data.delete('finish') if data.key?('finish') 245 | @metadata = data 246 | end 247 | 248 | # Render a part. 249 | def render_part(part, scope, locals={}, &content) 250 | formats = part.formatting(&@format) 251 | 252 | formats = formats.flatten.compact.uniq 253 | 254 | formats.reject!(&@reject) if @reject 255 | formats.select!(&@select) if @select 256 | 257 | formats.inject(part.text) do |text, format| 258 | factory.render(text, format, scope, locals, &content) 259 | end 260 | end 261 | 262 | # Get cached {Factory} instance. 263 | def factory 264 | @factory ||= Factory.new(:tilt=>@tilt) 265 | end 266 | 267 | end 268 | 269 | end 270 | --------------------------------------------------------------------------------