`.
#
# 2. A comment on the very first line of the code block in the format
# `#!language` where `language` is the language the code is in. For
# example, `#!ruby`.
#
# Options for individual colorizers will be taken from the {#run}
# options’ value for the given colorizer. For example, if the filter is
# invoked with a `:coderay => coderay_options_hash` option, the
# `coderay_options_hash` hash will be passed to the CodeRay colorizer.
#
# Currently, the following colorizers are supported:
#
# * `:coderay` for [Coderay](http://coderay.rubychan.de/)
# * `:pygmentize` for [pygmentize](http://pygments.org/docs/cmdline/), the
# command-line frontend for [Pygments](http://pygments.org/)
# * `:pygmentsrb` for [pygments.rb](https://github.com/tmm1/pygments.rb),
# a Ruby interface for [Pygments](http://pygments.org/)
# * `:simon_highlight` for [Highlight](http://www.andre-simon.de/doku/highlight/en/highlight.html)
# * `:rouge` for [Rouge](https://github.com/jayferd/rouge/)
#
# Additional colorizer implementations are welcome!
#
# @example Using a class to indicate type of code be highlighted
#
#
# def foo
# "asdf"
# end
#
#
# @example Using a comment to indicate type of code be highlighted
#
#
# #!ruby
# def foo
# "asdf"
# end
#
#
# @example Invoking the filter with custom parameters
#
# filter :colorize_syntax,
# :colorizers => { :ruby => :coderay },
# :coderay => { :line_numbers => :list }
#
# @param [String] content The content to filter
#
# @option params [Symbol] :default_colorizer (DEFAULT_COLORIZER) The
# default colorizer, i.e. the colorizer that will be used when the
# colorizer is not overriden for a specific language.
#
# @option params [Symbol] :syntax (:html) The syntax to use, which can be
# `:html`, `:xml` or `:xhtml`, the latter two being the same.
#
# @option params [Hash] :colorizers ({}) A hash containing
# a mapping of programming languages (symbols, not strings) onto
# colorizers (symbols).
#
# @option params [Boolean] :outside_pre (false) `true` if the colorizer
# should be applied on `code` elements outside `pre` elements, false
# if only `code` elements inside` pre` elements should be colorized.
#
# @option params [Symbol] :is_fullpage (false) Whether to treat the input
# as a full HTML page or a page fragment. When true, HTML boilerplate
# such as the doctype, `html`, `head` and `body` elements will be added.
#
# @return [String] The filtered content
def run(content, params = {})
Nanoc::Extra::JRubyNokogiriWarner.check_and_warn
# Take colorizers from parameters
@colorizers = Hash.new(params[:default_colorizer] || DEFAULT_COLORIZER)
(params[:colorizers] || {}).each_pair do |language, colorizer|
@colorizers[language] = colorizer
end
# Determine syntax (HTML or XML)
syntax = params[:syntax] || :html
case syntax
when :html
klass = Nokogiri::HTML
when :xml, :xhtml
klass = Nokogiri::XML
else
raise "unknown syntax: #{syntax.inspect} (expected :html or :xml)"
end
# Colorize
doc = parse(content, klass, params.fetch(:is_fullpage, false))
selector = params[:outside_pre] ? 'code' : 'pre > code'
doc.css(selector).each do |element|
# Get language
has_class = false
language = nil
if element['class']
# Get language from class
match = element['class'].match(/(^| )language-([^ ]+)/)
language = match[2] if match
has_class = true if language
else
# Get language from comment line
match = element.inner_text.strip.split[0].match(/^#!([^\/][^\n]*)$/)
language = match[1] if match
element.content = element.content.sub(/^#!([^\/][^\n]*)$\n/, '') if language
end
# Give up if there is no hope left
next if language.nil?
# Highlight
raw = strip(element.inner_text)
highlighted_code = highlight(raw, language, params)
element.children = Nokogiri::HTML.fragment(strip(highlighted_code), 'utf-8')
# Add language-something class
unless has_class
klass = element['class'] || ''
klass << ' ' unless [' ', nil].include?(klass[-1, 1])
klass << "language-#{language}"
element['class'] = klass
end
highlight_postprocess(language, element.parent)
end
method = "to_#{syntax}".to_sym
doc.send(method, encoding: 'UTF-8')
end
# Parses the given content using the given class. This method also handles
# an issue with Nokogiri on JRuby causing “cannot modify frozen string”
# errors.
#
# @param [String] content The content to parse
#
# @param [Class] klass The Nokogiri parser class (either Nokogiri::HTML
# or Nokogiri::XML)
#
# @param [Boolean] is_fullpage true if the given content is a full page,
# false if it is a fragment
def parse(content, klass, is_fullpage)
if is_fullpage
klass.parse(content, nil, 'UTF-8')
else
klass.fragment(content)
end
rescue => e
if e.message =~ /can't modify frozen string/
parse(content.dup, klass, is_fullpage)
else
raise e
end
end
# Runs the code through [CodeRay](http://coderay.rubychan.de/).
#
# @param [String] code The code to colorize
#
# @param [String] language The language the code is written in
#
# @param [Hash] params Parameters to pass on to CodeRay
#
# @return [String] The colorized output
def coderay(code, language, params = {})
require 'coderay'
::CodeRay.scan(code, language).html(params)
end
# Returns the input itself, not performing any code highlighting.
#
# @param [String] code The code to colorize
#
# @param [String] language The language the code is written in (unused)
#
# @return [String] The colorized output, which is identical to the input
# in this case
def dummy(code, language, params = {}) # rubocop:disable Lint/UnusedMethodArgument
code
end
# Runs the content through [pygmentize](http://pygments.org/docs/cmdline/),
# the command-line frontend for [Pygments](http://pygments.org/).
#
# @param [String] code The code to colorize
#
# @param [String] language The language the code is written in
#
# @option params [String, Symbol] :encoding The encoding of the code block
#
# @return [String] The colorized output
def pygmentize(code, language, params = {})
check_availability('pygmentize', '-V')
params[:encoding] ||= 'utf-8'
params[:nowrap] ||= 'True'
cmd = ['pygmentize', '-l', language, '-f', 'html']
cmd << '-O' << params.map { |k, v| "#{k}=#{v}" }.join(',') unless params.empty?
stdout = StringIO.new
stderr = $stderr
piper = Nanoc::Extra::Piper.new(stdout: stdout, stderr: stderr)
piper.run(cmd, code)
stdout.string
end
# Runs the content through [Pygments](http://pygments.org/) via
# [pygments.rb](https://github.com/tmm1/pygments.rb).
#
# @param [String] code The code to colorize
#
# @param [String] language The language the code is written in
#
# @return [String] The colorized output
def pygmentsrb(code, language, params = {})
require 'pygments'
args = params.dup
args[:lexer] ||= language
args[:options] ||= {}
args[:options][:encoding] ||= 'utf-8'
args[:options][:nowrap] ||= 'True'
Pygments.highlight(code, args)
end
SIMON_HIGHLIGHT_OPT_MAP = {
wrap: '-W',
include_style: '-I',
line_numbers: '-l',
}.freeze
# Runs the content through [Highlight](http://www.andre-simon.de/doku/highlight/en/highlight.html).
#
# @since 3.2.0
#
# @param [String] code The code to colorize
#
# @param [String] language The language the code is written in
#
# @option params [String] :style The style to use
#
# @return [String] The colorized output
def simon_highlight(code, language, params = {})
check_availability('highlight', '--version')
cmd = ['highlight', '--syntax', language, '--fragment']
params.each do |key, _value|
if SIMON_HIGHLIGHT_OPT_MAP[key]
cmd << SIMON_HIGHLIGHT_OPT_MAP[key]
else
# TODO: allow passing other options
case key
when :style
cmd << '--style' << params[:style]
end
end
end
stdout = StringIO.new
stderr = $stderr
piper = Nanoc::Extra::Piper.new(stdout: stdout, stderr: stderr)
piper.run(cmd, code)
stdout.string
end
# Wraps the element in
def coderay_postprocess(_language, element)
# Skip if we're a free
return if element.parent.nil?
#
div_inner = Nokogiri::XML::Node.new('div', element.document)
div_inner['class'] = 'code'
div_inner.children = element.dup
#
div_outer = Nokogiri::XML::Node.new('div', element.document)
div_outer['class'] = 'CodeRay'
div_outer.children = div_inner
# orig element
element.swap div_outer
end
# Runs the content through [Rouge](https://github.com/jayferd/rouge/.
#
# @param [String] code The code to colorize
#
# @param [String] language The language the code is written in
#
# @return [String] The colorized output
def rouge(code, language, params = {})
require 'rouge'
formatter_options = {
css_class: params.fetch(:css_class, 'highlight'),
}
formatter = Rouge::Formatters::HTML.new(formatter_options)
lexer = Rouge::Lexer.find_fancy(language, code) || Rouge::Lexers::PlainText
formatter.format(lexer.lex(code))
end
# Removes the double wrapping.
#
# Before:
#
#
#
# After:
#
#
def rouge_postprocess(_language, element)
return if element.name != 'pre'
code1 = element.xpath('code').first
return if code1.nil?
pre = code1.xpath('pre').first
return if pre.nil?
code2 = pre.xpath('code').first
return if code2.nil?
code1.inner_html = code2.inner_html
code1['class'] = [code1['class'], pre['class']].compact.join(' ')
end
protected
KNOWN_COLORIZERS = [:coderay, :dummy, :pygmentize, :pygmentsrb, :simon_highlight, :rouge].freeze
# Removes the first blank lines and any whitespace at the end.
def strip(s)
s.lines.drop_while { |line| line.strip.empty? }.join.rstrip
end
def highlight(code, language, params = {})
colorizer = @colorizers[language.to_sym]
if KNOWN_COLORIZERS.include?(colorizer)
send(colorizer, code, language, params[colorizer] || {})
else
raise "I don’t know how to highlight code using the “#{colorizer}” colorizer"
end
end
def highlight_postprocess(language, element)
colorizer = @colorizers[language.to_sym]
if KNOWN_COLORIZERS.include?(colorizer)
sym = (colorizer.to_s + '_postprocess').to_sym
if respond_to?(sym)
send(sym, language, element)
end
else
raise "I don’t know how to highlight code using the “#{colorizer}” colorizer"
end
end
def check_availability(*cmd)
piper = Nanoc::Extra::Piper.new(stdout: StringIO.new, stderr: StringIO.new)
piper.run(cmd, nil)
end
end
end
nanoc-4.1.4/lib/nanoc/filters/pandoc.rb0000644000004100000410000000222012665031555017757 0ustar www-datawww-datamodule Nanoc::Filters
# @api private
class Pandoc < Nanoc::Filter
requires 'pandoc-ruby'
# Runs the content through [Pandoc](http://johnmacfarlane.net/pandoc/)
# using [PandocRuby](https://github.com/alphabetum/pandoc-ruby).
#
# Arguments can be passed to PandocRuby in two ways:
#
# * Use the `:args` option. This approach is more flexible, since it
# allows passing an array instead of a hash.
#
# * Pass the arguments directly to the filter. With this approach, only
# hashes can be passed, which is more limiting than the `:args` approach.
#
# The `:args` approach is recommended.
#
# @example Passing arguments using `:arg`
#
# filter :pandoc, args: [:s, {:f => :markdown, :to => :html}, 'no-wrap', :toc]
#
# @example Passing arguments not using `:arg`
#
# filter :pandoc, :f => :markdown, :to => :html
#
# @param [String] content The content to filter
#
# @return [String] The filtered content
def run(content, params = {})
args = params.key?(:args) ? params[:args] : params
PandocRuby.convert(content, *args)
end
end
end
nanoc-4.1.4/lib/nanoc/filters/kramdown.rb0000644000004100000410000000111612665031555020340 0ustar www-datawww-datamodule Nanoc::Filters
# @api private
class Kramdown < Nanoc::Filter
requires 'kramdown'
# Runs the content through [Kramdown](http://kramdown.gettalong.org/).
# Parameters passed to this filter will be passed on to Kramdown.
#
# @param [String] content The content to filter
#
# @return [String] The filtered content
def run(content, params = {})
document = ::Kramdown::Document.new(content, params)
document.warnings.each do |warning|
$stderr.puts "kramdown warning: #{warning}"
end
document.to_html
end
end
end
nanoc-4.1.4/lib/nanoc/filters/erubis.rb0000644000004100000410000000172212665031555020012 0ustar www-datawww-datamodule Nanoc::Filters
# @api private
class Erubis < Nanoc::Filter
requires 'erubis'
# The same as `::Erubis::Eruby` but adds `_erbout` as an alias for the
# `_buf` variable, making it compatible with Nanoc’s helpers that rely
# on `_erbout`, such as {Nanoc::Helpers::Capturing}.
class ErubisWithErbout < ::Erubis::Eruby
include ::Erubis::ErboutEnhancer
end
# Runs the content through [Erubis](http://www.kuwata-lab.com/erubis/).
# This method takes no options.
#
# @param [String] content The content to filter
#
# @return [String] The filtered content
def run(content, _params = {})
# Create context
context = ::Nanoc::Int::Context.new(assigns)
# Get binding
proc = assigns[:content] ? -> { assigns[:content] } : nil
assigns_binding = context.get_binding(&proc)
# Get result
ErubisWithErbout.new(content, filename: filename).result(assigns_binding)
end
end
end
nanoc-4.1.4/lib/nanoc/filters/slim.rb0000644000004100000410000000127712665031555017472 0ustar www-datawww-datamodule Nanoc::Filters
# @since 3.2.0
#
# @api private
class Slim < Nanoc::Filter
requires 'slim'
# Runs the content through [Slim](http://slim-lang.com/).
# This method takes no options.
#
# @param [String] content The content to filter
#
# @return [String] The filtered content
def run(content, params = {})
params = {
disable_capture: true, # Capture managed by Nanoc
buffer: '_erbout', # Force slim to output to the buffer used by Nanoc
}.merge params
# Create context
context = ::Nanoc::Int::Context.new(assigns)
::Slim::Template.new(params) { content }.render(context) { assigns[:content] }
end
end
end
nanoc-4.1.4/lib/nanoc/filters/yui_compressor.rb0000644000004100000410000000114612665031555021603 0ustar www-datawww-datamodule Nanoc::Filters
# @since 3.3.0
#
# @api private
class YUICompressor < Nanoc::Filter
requires 'yuicompressor'
# Compress Javascript or CSS using [YUICompressor](http://rubydoc.info/gems/yuicompressor).
# This method optionally takes options to pass directly to the
# YUICompressor gem.
#
# @param [String] content JavaScript or CSS input
#
# @param [Hash] params Options passed to YUICompressor
#
# @return [String] Compressed but equivalent JavaScript or CSS
def run(content, params = {})
::YUICompressor.compress(content, params)
end
end
end
nanoc-4.1.4/lib/nanoc/filters/rubypants.rb0000644000004100000410000000066312665031555020553 0ustar www-datawww-datamodule Nanoc::Filters
# @api private
class RubyPants < Nanoc::Filter
requires 'rubypants'
# Runs the content through [RubyPants](http://rubydoc.info/gems/rubypants/).
# This method takes no options.
#
# @param [String] content The content to filter
#
# @return [String] The filtered content
def run(content, _params = {})
# Get result
::RubyPants.new(content).to_html
end
end
end
nanoc-4.1.4/lib/nanoc/filters/maruku.rb0000644000004100000410000000071512665031555020026 0ustar www-datawww-datamodule Nanoc::Filters
# @api private
class Maruku < Nanoc::Filter
requires 'maruku'
# Runs the content through [Maruku](https://github.com/bhollis/maruku/).
# Parameters passed to this filter will be passed on to Maruku.
#
# @param [String] content The content to filter
#
# @return [String] The filtered content
def run(content, params = {})
# Get result
::Maruku.new(content, params).to_html
end
end
end
nanoc-4.1.4/lib/nanoc/filters/uglify_js.rb0000644000004100000410000000110312665031555020505 0ustar www-datawww-datamodule Nanoc::Filters
# @api private
class UglifyJS < Nanoc::Filter
requires 'uglifier'
# Runs the content through [UglifyJS](https://github.com/mishoo/UglifyJS2/).
# This method optionally takes options to pass directly to Uglifier.
#
# @param [String] content The content to filter
#
# @option params [Array] :options ([]) A list of options to pass on to Uglifier
#
# @return [String] The filtered content
def run(content, params = {})
# Add filename to load path
Uglifier.new(params).compile(content)
end
end
end
nanoc-4.1.4/lib/nanoc/filters.rb0000644000004100000410000000661112665031555016523 0ustar www-datawww-data# @api private
module Nanoc::Filters
autoload 'AsciiDoc', 'nanoc/filters/asciidoc'
autoload 'BlueCloth', 'nanoc/filters/bluecloth'
autoload 'ColorizeSyntax', 'nanoc/filters/colorize_syntax'
autoload 'CoffeeScript', 'nanoc/filters/coffeescript'
autoload 'ERB', 'nanoc/filters/erb'
autoload 'Erubis', 'nanoc/filters/erubis'
autoload 'Haml', 'nanoc/filters/haml'
autoload 'Handlebars', 'nanoc/filters/handlebars'
autoload 'Kramdown', 'nanoc/filters/kramdown'
autoload 'Less', 'nanoc/filters/less'
autoload 'Markaby', 'nanoc/filters/markaby'
autoload 'Maruku', 'nanoc/filters/maruku'
autoload 'Mustache', 'nanoc/filters/mustache'
autoload 'Pandoc', 'nanoc/filters/pandoc'
autoload 'Rainpress', 'nanoc/filters/rainpress'
autoload 'RDiscount', 'nanoc/filters/rdiscount'
autoload 'RDoc', 'nanoc/filters/rdoc'
autoload 'Redcarpet', 'nanoc/filters/redcarpet'
autoload 'RedCloth', 'nanoc/filters/redcloth'
autoload 'RelativizePaths', 'nanoc/filters/relativize_paths'
autoload 'RubyPants', 'nanoc/filters/rubypants'
autoload 'Sass', 'nanoc/filters/sass'
autoload 'Slim', 'nanoc/filters/slim'
autoload 'Typogruby', 'nanoc/filters/typogruby'
autoload 'UglifyJS', 'nanoc/filters/uglify_js'
autoload 'XSL', 'nanoc/filters/xsl'
autoload 'YUICompressor', 'nanoc/filters/yui_compressor'
Nanoc::Filter.register '::Nanoc::Filters::AsciiDoc', :asciidoc
Nanoc::Filter.register '::Nanoc::Filters::BlueCloth', :bluecloth
Nanoc::Filter.register '::Nanoc::Filters::ColorizeSyntax', :colorize_syntax
Nanoc::Filter.register '::Nanoc::Filters::CoffeeScript', :coffeescript
Nanoc::Filter.register '::Nanoc::Filters::ERB', :erb
Nanoc::Filter.register '::Nanoc::Filters::Erubis', :erubis
Nanoc::Filter.register '::Nanoc::Filters::Haml', :haml
Nanoc::Filter.register '::Nanoc::Filters::Handlebars', :handlebars
Nanoc::Filter.register '::Nanoc::Filters::Kramdown', :kramdown
Nanoc::Filter.register '::Nanoc::Filters::Less', :less
Nanoc::Filter.register '::Nanoc::Filters::Markaby', :markaby
Nanoc::Filter.register '::Nanoc::Filters::Maruku', :maruku
Nanoc::Filter.register '::Nanoc::Filters::Mustache', :mustache
Nanoc::Filter.register '::Nanoc::Filters::Pandoc', :pandoc
Nanoc::Filter.register '::Nanoc::Filters::Rainpress', :rainpress
Nanoc::Filter.register '::Nanoc::Filters::RDiscount', :rdiscount
Nanoc::Filter.register '::Nanoc::Filters::RDoc', :rdoc
Nanoc::Filter.register '::Nanoc::Filters::Redcarpet', :redcarpet
Nanoc::Filter.register '::Nanoc::Filters::RedCloth', :redcloth
Nanoc::Filter.register '::Nanoc::Filters::RelativizePaths', :relativize_paths
Nanoc::Filter.register '::Nanoc::Filters::RubyPants', :rubypants
Nanoc::Filter.register '::Nanoc::Filters::Sass', :sass
Nanoc::Filter.register '::Nanoc::Filters::Slim', :slim
Nanoc::Filter.register '::Nanoc::Filters::Typogruby', :typogruby
Nanoc::Filter.register '::Nanoc::Filters::UglifyJS', :uglify_js
Nanoc::Filter.register '::Nanoc::Filters::XSL', :xsl
Nanoc::Filter.register '::Nanoc::Filters::YUICompressor', :yui_compressor
end
nanoc-4.1.4/lib/nanoc/cli.rb0000644000004100000410000001332012665031555015615 0ustar www-datawww-databegin
require 'cri'
rescue LoadError => e
$stderr.puts e
$stderr.puts "If you are using a Gemfile, make sure that the Gemfile contains Nanoc ('gem \"nanoc\"')."
exit 1
end
if Nanoc.on_windows?
begin
require 'Win32/Console/ANSI'
rescue LoadError
end
end
# @api private
module Nanoc::CLI
module Commands
end
autoload 'ANSIStringColorizer', 'nanoc/cli/ansi_string_colorizer'
autoload 'Logger', 'nanoc/cli/logger'
autoload 'CommandRunner', 'nanoc/cli/command_runner'
autoload 'CleaningStream', 'nanoc/cli/cleaning_stream'
autoload 'StreamCleaners', 'nanoc/cli/stream_cleaners'
autoload 'ErrorHandler', 'nanoc/cli/error_handler'
# @return [Boolean] true if debug output is enabled, false if not
#
# @since 3.2.0
def self.debug?
@debug || false
end
# @param [Boolean] boolean true if debug output should be enabled,
# false if it should not
#
# @return [void]
#
# @since 3.2.0
def self.debug=(boolean)
@debug = boolean
end
# Invokes the Nanoc command-line tool with the given arguments.
#
# @param [Array] args An array of command-line arguments
#
# @return [void]
def self.run(args)
Nanoc::CLI::ErrorHandler.handle_while do
setup
root_command.run(args)
end
end
# @return [Cri::Command] The root command, i.e. the command-line tool itself
def self.root_command
@root_command
end
# Adds the given command to the collection of available commands.
#
# @param [Cri::Command] cmd The command to add
#
# @return [void]
def self.add_command(cmd)
root_command.add_command(cmd)
end
# Schedules the given block to be executed after the CLI has been set up.
#
# @return [void]
def self.after_setup(&block)
# TODO: decide what should happen if the CLI is already set up
add_after_setup_proc(block)
end
protected
# Makes the command-line interface ready for use.
#
# @return [void]
def self.setup
setup_cleaning_streams
setup_commands
load_custom_commands
after_setup_procs.each(&:call)
end
# Sets up the root command and base subcommands.
#
# @return [void]
def self.setup_commands
# Reinit
@root_command = nil
# Add root command
filename = File.dirname(__FILE__) + '/cli/commands/nanoc.rb'
@root_command = load_command_at(filename)
# Add help command
help_cmd = Cri::Command.new_basic_help
add_command(help_cmd)
# Add other commands
cmd_filenames = Dir[File.dirname(__FILE__) + '/cli/commands/*.rb']
cmd_filenames.each do |cmd_filename|
next if File.basename(cmd_filename, '.rb') == 'nanoc'
cmd = load_command_at(cmd_filename)
add_command(cmd)
end
end
# Loads site-specific commands.
#
# @return [void]
def self.load_custom_commands
if Nanoc::Int::SiteLoader.cwd_is_nanoc_site?
config = Nanoc::Int::ConfigLoader.new.new_from_cwd
config[:commands_dirs].each do |path|
load_commands_at(path)
end
end
end
def self.load_commands_at(path)
recursive_contents_of(path).each do |filename|
# Create command
command = Nanoc::CLI.load_command_at(filename)
# Get supercommand
pieces = filename.gsub(/^#{path}\/|\.rb$/, '').split('/')
pieces = pieces[0, pieces.size - 1] || []
root = Nanoc::CLI.root_command
supercommand = pieces.reduce(root) do |cmd, piece|
cmd.nil? ? nil : cmd.command_named(piece)
end
# Add to supercommand
if supercommand.nil?
raise "Cannot load command at #{filename} because its supercommand cannot be found"
end
supercommand.add_command(command)
end
end
# Loads the command in the file with the given filename.
#
# @param [String] filename The name of the file that contains the command
#
# @return [Cri::Command] The loaded command
def self.load_command_at(filename, command_name = nil)
# Load
code = File.read(filename, encoding: 'UTF-8')
cmd = Cri::Command.define(code, filename)
# Set name
command_name ||= File.basename(filename, '.rb')
cmd.modify { name command_name }
# Done
cmd
end
# @return [Array] The directory contents
def self.recursive_contents_of(path)
return [] unless File.directory?(path)
files, dirs = *Dir[path + '/*'].sort.partition { |e| File.file?(e) }
dirs.each { |d| files.concat recursive_contents_of(d) }
files
end
# Wraps the given stream in a cleaning stream. The cleaning streams will
# have the proper stream cleaners configured.
#
# @param [IO] io The stream to wrap
#
# @return [::Nanoc::CLI::CleaningStream]
def self.wrap_in_cleaning_stream(io)
cio = ::Nanoc::CLI::CleaningStream.new(io)
unless enable_utf8?(io)
cio.add_stream_cleaner(Nanoc::CLI::StreamCleaners::UTF8)
end
unless enable_ansi_colors?(io)
cio.add_stream_cleaner(Nanoc::CLI::StreamCleaners::ANSIColors)
end
cio
end
# Wraps `$stdout` and `$stderr` in appropriate cleaning streams.
#
# @return [void]
def self.setup_cleaning_streams
$stdout = wrap_in_cleaning_stream($stdout)
$stderr = wrap_in_cleaning_stream($stderr)
end
# @return [Boolean] true if UTF-8 support is present, false if not
def self.enable_utf8?(io)
return true unless io.tty?
%w( LC_ALL LC_CTYPE LANG ).any? { |e| ENV[e] =~ /UTF/i }
end
# @return [Boolean] true if color support is present, false if not
def self.enable_ansi_colors?(io)
unless io.tty?
return false
end
if Nanoc.on_windows?
return defined?(::Win32::Console::ANSI)
end
true
end
def self.after_setup_procs
@after_setup_procs || []
end
def self.add_after_setup_proc(proc)
@after_setup_procs ||= []
@after_setup_procs << proc
end
end
nanoc-4.1.4/lib/nanoc/helpers.rb0000644000004100000410000000103612665031555016511 0ustar www-datawww-datamodule Nanoc::Helpers
autoload 'Blogging', 'nanoc/helpers/blogging'
autoload 'Breadcrumbs', 'nanoc/helpers/breadcrumbs'
autoload 'Capturing', 'nanoc/helpers/capturing'
autoload 'Filtering', 'nanoc/helpers/filtering'
autoload 'HTMLEscape', 'nanoc/helpers/html_escape'
autoload 'LinkTo', 'nanoc/helpers/link_to'
autoload 'Rendering', 'nanoc/helpers/rendering'
autoload 'Tagging', 'nanoc/helpers/tagging'
autoload 'Text', 'nanoc/helpers/text'
autoload 'XMLSitemap', 'nanoc/helpers/xml_sitemap'
end
nanoc-4.1.4/lib/nanoc/version.rb0000644000004100000410000000011312665031555016527 0ustar www-datawww-datamodule Nanoc
# The current Nanoc version.
VERSION = '4.1.4'.freeze
end
nanoc-4.1.4/lib/nanoc/extra/0000755000004100000410000000000012665031555015645 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/extra/core_ext.rb0000644000004100000410000000011412665031555017776 0ustar www-datawww-datarequire 'nanoc/extra/core_ext/pathname'
require 'nanoc/extra/core_ext/time'
nanoc-4.1.4/lib/nanoc/extra/jruby_nokogiri_warner.rb0000644000004100000410000000223612665031555022607 0ustar www-datawww-datarequire 'singleton'
module Nanoc::Extra
# @api private
class JRubyNokogiriWarner
include Singleton
TEXT = < :href,
'audio' => :src,
'form' => :action,
'iframe' => :src,
'img' => :src,
'link' => :href,
'script' => :src,
'video' => :src,
}.freeze
def initialize(filenames, mode = nil)
Nanoc::Extra::JRubyNokogiriWarner.check_and_warn
@filenames = filenames
@filter =
case mode
when nil
->(_h) { true }
when :external
->(h) { external_href?(h) }
when :internal
->(h) { !external_href?(h) }
else
raise ArgumentError, 'Expected mode argument to be :internal, :external or nil'
end
end
def filenames_per_href
require 'nokogiri'
filenames_per_href = {}
@filenames.each do |filename|
hrefs_in_file(filename).each do |href|
filenames_per_href[href] ||= Set.new
filenames_per_href[href] << filename
end
end
filenames_per_href
end
def filenames_per_resource_uri
require 'nokogiri'
filenames_per_resource_uri = {}
@filenames.each do |filename|
resource_uris_in_file(filename).each do |resouce_uri|
filenames_per_resource_uri[resouce_uri] ||= Set.new
filenames_per_resource_uri[resouce_uri] << filename
end
end
filenames_per_resource_uri
end
def external_href?(href)
href =~ %r{^(\/\/|[a-z\-]+:)}
end
def hrefs_in_file(filename)
uris_in_file filename, %w(a img)
end
def resource_uris_in_file(filename)
uris_in_file filename, %w(audio form img iframe link script video)
end
private
def uris_in_file(filename, tag_names)
uris = Set.new
doc = Nokogiri::HTML(::File.read(filename))
tag_names.each do |tag_name|
attr = URI_ATTRS[tag_name]
doc.css(tag_name).each do |e|
uris << e[attr] unless e[attr].nil?
end
end
# Strip fragment
uris.map! { |href| href.gsub(/#.*$/, '') }
uris.select(&@filter)
end
end
end
nanoc-4.1.4/lib/nanoc/extra/core_ext/0000755000004100000410000000000012665031555017455 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/extra/core_ext/time.rb0000644000004100000410000000057512665031555020747 0ustar www-datawww-data# @api private
module Nanoc::Extra::TimeExtensions
# @return [String] The time in an ISO-8601 date format.
def __nanoc_to_iso8601_date
strftime('%Y-%m-%d')
end
# @return [String] The time in an ISO-8601 time format.
def __nanoc_to_iso8601_time
getutc.strftime('%Y-%m-%dT%H:%M:%SZ')
end
end
# @api private
class Time
include Nanoc::Extra::TimeExtensions
end
nanoc-4.1.4/lib/nanoc/extra/core_ext/pathname.rb0000644000004100000410000000076312665031555021605 0ustar www-datawww-datamodule Nanoc::Extra
# @api private
module PathnameExtensions
def __nanoc_components
components = []
tmp = self
loop do
old = tmp
components << File.basename(tmp)
tmp = File.dirname(tmp)
break if old == tmp
end
components.reverse
end
def __nanoc_include_component?(component)
__nanoc_components.include?(component)
end
end
end
# @api private
class ::Pathname
include ::Nanoc::Extra::PathnameExtensions
end
nanoc-4.1.4/lib/nanoc/extra/checking.rb0000644000004100000410000000047212665031555017750 0ustar www-datawww-datamodule Nanoc::Extra
# @api private
module Checking
autoload 'Check', 'nanoc/extra/checking/check'
autoload 'DSL', 'nanoc/extra/checking/dsl'
autoload 'Runner', 'nanoc/extra/checking/runner.rb'
autoload 'Issue', 'nanoc/extra/checking/issue'
end
end
require 'nanoc/extra/checking/checks'
nanoc-4.1.4/lib/nanoc/extra/piper.rb0000644000004100000410000000211112665031555017304 0ustar www-datawww-datarequire 'open3'
module Nanoc::Extra
# @api private
class Piper
class Error < ::Nanoc::Int::Errors::Generic
def initialize(command, exit_code)
@command = command
@exit_code = exit_code
end
def message
"command exited with a nonzero status code #{@exit_code} (command: #{@command.join(' ')})"
end
end
# @param [IO] stdout
# @param [IO] stderr
def initialize(stdout: $stdout, stderr: $stderr)
@stdout = stdout
@stderr = stderr
end
# @param [Array] cmd
#
# @param [String, nil] input
def run(cmd, input)
Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thr|
stdout_thread = Thread.new { @stdout << stdout.read }
stderr_thread = Thread.new { @stderr << stderr.read }
if input
stdin << input
end
stdin.close
stdout_thread.join
stderr_thread.join
exit_status = wait_thr.value
unless exit_status.success?
raise Error.new(cmd, exit_status.to_i)
end
end
end
end
end
nanoc-4.1.4/lib/nanoc/extra/deployers/0000755000004100000410000000000012665031555017653 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/extra/deployers/rsync.rb0000644000004100000410000000337612665031555021347 0ustar www-datawww-datamodule Nanoc::Extra::Deployers
# A deployer that deploys a site using rsync.
#
# The configuration has should include a `:dst` value, a string containing
# the destination to where rsync should upload its data. It will likely be
# in `host:path` format. It should not end with a slash. For example,
# `"example.com:/var/www/sites/mysite/html"`.
#
# @example A deployment configuration with public and staging configurations
#
# deploy:
# public:
# kind: rsync
# dst: "ectype:sites/stoneship/public"
# staging:
# kind: rsync
# dst: "ectype:sites/stoneship-staging/public"
# options: [ "-glpPrtvz" ]
#
# @api private
class Rsync < ::Nanoc::Extra::Deployer
# Default rsync options
DEFAULT_OPTIONS = [
'--group',
'--links',
'--perms',
'--partial',
'--progress',
'--recursive',
'--times',
'--verbose',
'--compress',
'--exclude=".hg"',
'--exclude=".svn"',
'--exclude=".git"',
].freeze
# @see Nanoc::Extra::Deployer#run
def run
# Get params
src = source_path + '/'
dst = config[:dst]
options = config[:options] || DEFAULT_OPTIONS
# Validate
raise 'No dst found in deployment configuration' if dst.nil?
raise 'dst requires no trailing slash' if dst[-1, 1] == '/'
# Run
if dry_run
warn 'Performing a dry-run; no actions will actually be performed'
run_shell_cmd(['echo', 'rsync', options, src, dst].flatten)
else
run_shell_cmd(['rsync', options, src, dst].flatten)
end
end
private
def run_shell_cmd(cmd)
piper = Nanoc::Extra::Piper.new(stdout: $stdout, stderr: $stderr)
piper.run(cmd, nil)
end
end
end
nanoc-4.1.4/lib/nanoc/extra/deployers/fog.rb0000644000004100000410000001026112665031555020753 0ustar www-datawww-datamodule Nanoc::Extra::Deployers
# A deployer that deploys a site using [fog](https://github.com/geemus/fog).
#
# @example A deployment configuration with public and staging configurations
#
# deploy:
# public:
# kind: fog
# bucket: nanoc-site
# cdn_id: XXXXXX
# preprod:
# kind: fog
# provider: local
# local_root: ~/myCloud
# bucket: nanoc-site
# staging:
# kind: fog
# provider: local
# local_root: ~/myCloud
# bucket: nanoc-site-staging
#
# @api private
class Fog < ::Nanoc::Extra::Deployer
# @see Nanoc::Extra::Deployer#run
def run
require 'fog'
# Get params, unsetting anything we don't want to pass through to fog.
src = File.expand_path(source_path)
bucket = config.delete(:bucket) || config.delete(:bucket_name)
path = config.delete(:path)
cdn_id = config.delete(:cdn_id)
config.delete(:kind)
# Validate params
error 'The path requires no trailing slash' if path && path[-1, 1] == '/'
# Mock if necessary
if dry_run?
puts 'Dry run - simulation'
::Fog.mock!
end
# Get connection
puts 'Connecting'
connection = ::Fog::Storage.new(config)
# Get bucket
puts 'Getting bucket'
begin
directory = connection.directories.get(bucket, prefix: path)
rescue ::Excon::Errors::NotFound
should_create_bucket = true
end
should_create_bucket = true if directory.nil?
# Create bucket if necessary
if should_create_bucket
puts 'Creating bucket'
directory = connection.directories.create(key: bucket, prefix: path)
end
# Get list of remote files
files = directory.files
truncated = files.respond_to?(:is_truncated) && files.is_truncated
while truncated
set = directory.files.all(marker: files.last.key)
truncated = set.is_truncated
files += set
end
keys_to_destroy = files.map(&:key)
keys_to_invalidate = []
etags = read_etags(files)
# Upload all the files in the output folder to the clouds
puts 'Uploading local files'
FileUtils.cd(src) do
files = Dir['**/*'].select { |f| File.file?(f) }
files.each do |file_path|
key = path ? File.join(path, file_path) : file_path
upload(key, file_path, etags, keys_to_destroy, keys_to_invalidate, directory)
end
end
# delete extraneous remote files
puts 'Removing remote files'
keys_to_destroy.each do |key|
directory.files.get(key).destroy
end
# invalidate CDN objects
if cdn_id
puts 'Invalidating CDN distribution'
keys_to_invalidate.concat(keys_to_destroy)
cdn = ::Fog::CDN.new(config)
# fog cannot mock CDN requests
unless dry_run?
distribution = cdn.get_distribution(cdn_id)
# usual limit per invalidation: 1000 objects
keys_to_invalidate.each_slice(1000) do |paths|
cdn.post_invalidation(distribution, paths)
end
end
end
puts 'Done!'
end
private
def upload(key, file_path, etags, keys_to_destroy, keys_to_invalidate, dir)
keys_to_destroy.delete(key)
return unless needs_upload?(key, file_path, etags)
dir.files.create(
key: key,
body: File.open(file_path),
public: true)
keys_to_invalidate.push(key)
end
def needs_upload?(key, file_path, etags)
remote_etag = etags[key]
return true if remote_etag.nil?
local_etag = calc_local_etag(file_path)
remote_etag != local_etag
end
def read_etags(files)
case config[:provider]
when 'aws'
files.each_with_object({}) do |file, etags|
etags[file.key] = file.etag
end
else
{}
end
end
def calc_local_etag(file_path)
case config[:provider]
when 'aws'
Digest::MD5.file(file_path).hexdigest
end
end
# Prints the given message on stderr and exits.
def error(msg)
raise RuntimeError.new(msg)
end
end
end
nanoc-4.1.4/lib/nanoc/extra/deployer.rb0000644000004100000410000000256312665031555020023 0ustar www-datawww-datamodule Nanoc::Extra
# Represents a deployer, an object that allows uploading the compiled site
# to a specific (remote) location.
#
# @abstract Subclass and override {#run} to implement a custom filter.
#
# @api private
class Deployer
extend Nanoc::Int::PluginRegistry::PluginMethods
# @return [String] The path to the directory that contains the files to
# upload. It should not have a trailing slash.
attr_reader :source_path
# @return [Hash] The deployer configuration
attr_reader :config
# @return [Boolean] true if the deployer should only show what would be
# deployed instead of doing the actual deployment
attr_reader :dry_run
alias dry_run? dry_run
# @param [String] source_path The path to the directory that contains the
# files to upload. It should not have a trailing slash.
#
# @return [Hash] config The deployer configuration
#
# @param [Boolean] dry_run true if the deployer should
# only show what would be deployed instead actually deploying
def initialize(source_path, config, dry_run: false)
@source_path = source_path
@config = config
@dry_run = dry_run
end
# Performs the actual deployment.
#
# @abstract
def run
raise NotImplementedError.new('Nanoc::Extra::Deployer subclasses must implement #run')
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/0000755000004100000410000000000012665031555017420 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/extra/checking/issue.rb0000644000004100000410000000045612665031555021102 0ustar www-datawww-datamodule Nanoc::Extra::Checking
# @api private
class Issue
attr_reader :description
attr_reader :subject
attr_reader :check_class
def initialize(desc, subject, check_class)
@description = desc
@subject = subject
@check_class = check_class
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/check.rb0000644000004100000410000000267112665031555021030 0ustar www-datawww-datamodule Nanoc::Extra::Checking
# @api private
class OutputDirNotFoundError < Nanoc::Int::Errors::Generic
def initialize(directory_path)
super("Unable to run check against output directory at “#{directory_path}”: directory does not exist.")
end
end
# @api private
class Check < Nanoc::Int::Context
extend Nanoc::Int::PluginRegistry::PluginMethods
attr_reader :issues
def self.create(site)
output_dir = site.config[:output_dir]
unless File.exist?(output_dir)
raise Nanoc::Extra::Checking::OutputDirNotFoundError.new(output_dir)
end
output_filenames = Dir[output_dir + '/**/*'].select { |f| File.file?(f) }
# FIXME: ugly
view_context = site.compiler.create_view_context
context = {
items: Nanoc::ItemCollectionWithRepsView.new(site.items, view_context),
layouts: Nanoc::LayoutCollectionView.new(site.layouts, view_context),
config: Nanoc::ConfigView.new(site.config, view_context),
site: Nanoc::SiteView.new(site, view_context), # TODO: remove me
output_filenames: output_filenames,
}
new(context)
end
def initialize(context)
super(context)
@issues = Set.new
end
def run
raise NotImplementedError.new('Nanoc::Extra::Checking::Check subclasses must implement #run')
end
def add_issue(desc, subject: nil)
@issues << Issue.new(desc, subject, self.class)
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/checks/0000755000004100000410000000000012665031555020660 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/extra/checking/checks/internal_links.rb0000644000004100000410000000415312665031555024224 0ustar www-datawww-datarequire 'uri'
module Nanoc::Extra::Checking::Checks
# A check that verifies that all internal links point to a location that exists.
#
# @api private
class InternalLinks < ::Nanoc::Extra::Checking::Check
# Starts the validator. The results will be printed to stdout.
#
# Internal links that match a regexp pattern in `@config[:checks][:internal_links][:exclude]` will
# be skipped.
#
# @return [void]
def run
# TODO: de-duplicate this (duplicated in external links check)
filenames = output_filenames.select { |f| File.extname(f) == '.html' }
hrefs_with_filenames = ::Nanoc::Extra::LinkCollector.new(filenames, :internal).filenames_per_href
hrefs_with_filenames.each_pair do |href, fns|
fns.each do |filename|
next if valid?(href, filename)
add_issue(
"broken reference to #{href}",
subject: filename)
end
end
end
protected
def valid?(href, origin)
# Skip hrefs that point to self
# FIXME: this is ugly and won’t always be correct
return true if href == '.'
# Skip hrefs that are specified in the exclude configuration
return true if excluded?(href)
# Remove target
path = href.sub(/#.*$/, '')
return true if path.empty?
# Remove query string
path = path.sub(/\?.*$/, '')
return true if path.empty?
# Decode URL (e.g. '%20' -> ' ')
path = URI.unescape(path)
# Make absolute
path =
if path[0, 1] == '/'
@config[:output_dir] + path
else
::File.expand_path(path, ::File.dirname(origin))
end
# Check whether file exists
return true if File.file?(path)
# Check whether directory with index file exists
return true if File.directory?(path) && @config[:index_filenames].any? { |fn| File.file?(File.join(path, fn)) }
# Nope :(
false
end
def excluded?(href)
excludes = @config.fetch(:checks, {}).fetch(:internal_links, {}).fetch(:exclude, [])
excludes.any? { |pattern| Regexp.new(pattern).match(href) }
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/checks/mixed_content.rb0000644000004100000410000000162012665031555024044 0ustar www-datawww-datamodule Nanoc::Extra::Checking::Checks
# A check that verifies HTML files do not reference external resources with
# URLs that would trigger "mixed content" warnings.
#
# @api private
class MixedContent < ::Nanoc::Extra::Checking::Check
PROTOCOL_PATTERN = /^(\w+):\/\//
def run
filenames = output_filenames.select { |f| File.extname(f) == '.html' }
resource_uris_with_filenames = ::Nanoc::Extra::LinkCollector.new(filenames).filenames_per_resource_uri
resource_uris_with_filenames.each_pair do |uri, fns|
next unless guaranteed_insecure?(uri)
fns.each do |filename|
add_issue(
"mixed content include: #{uri}",
subject: filename)
end
end
end
private
def guaranteed_insecure?(href)
protocol = PROTOCOL_PATTERN.match(href)
protocol && protocol[1].casecmp('http').zero?
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/checks/css.rb0000644000004100000410000000124112665031555021773 0ustar www-datawww-datamodule ::Nanoc::Extra::Checking::Checks
# @api private
class CSS < ::Nanoc::Extra::Checking::Check
identifier :css
def run
require 'w3c_validators'
Dir[@config[:output_dir] + '/**/*.css'].each do |filename|
results = ::W3CValidators::CSSValidator.new.validate_file(filename)
lines = File.readlines(filename)
results.errors.each do |e|
line_num = e.line.to_i - 1
line = lines[line_num]
message = e.message.gsub(%r{\s+}, ' ').strip.sub(/\s+:$/, '')
desc = "line #{line_num + 1}: #{message}: #{line}"
add_issue(desc, subject: filename)
end
end
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/checks/stale.rb0000644000004100000410000000140112665031555022311 0ustar www-datawww-datamodule Nanoc::Extra::Checking::Checks
# @api private
class Stale < ::Nanoc::Extra::Checking::Check
def run
require 'set'
output_filenames.each do |f|
next if pruner.filename_excluded?(f)
next if item_rep_paths.include?(f)
add_issue(
'file without matching item',
subject: f)
end
end
protected
def item_rep_paths
@item_rep_paths ||=
Set.new(
@items
.flat_map(&:reps)
.map(&:unwrap)
.flat_map(&:raw_paths)
.flat_map(&:values))
end
def pruner
exclude_config = @config.fetch(:prune, {}).fetch(:exclude, [])
@pruner ||= Nanoc::Extra::Pruner.new(@site, exclude: exclude_config)
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/checks/external_links.rb0000644000004100000410000001047612665031555024237 0ustar www-datawww-datarequire 'net/http'
require 'net/https'
require 'nokogiri'
require 'timeout'
require 'uri'
module ::Nanoc::Extra::Checking::Checks
# A validator that verifies that all external links point to a location that exists.
#
# @api private
class ExternalLinks < ::Nanoc::Extra::Checking::Check
identifiers :external_links, :elinks
def run
# Find all broken external hrefs
# TODO: de-duplicate this (duplicated in internal links check)
filenames = output_filenames.select { |f| File.extname(f) == '.html' && !excluded_file?(f) }
hrefs_with_filenames = ::Nanoc::Extra::LinkCollector.new(filenames, :external).filenames_per_href
results = select_invalid(hrefs_with_filenames.keys)
# Report them
results.each do |res|
filenames = hrefs_with_filenames[res.href]
filenames.each do |filename|
add_issue(
"broken reference to #{res.href}: #{res.explanation}",
subject: filename)
end
end
end
class Result
attr_reader :href
attr_reader :explanation
def initialize(href, explanation)
@href = href
@explanation = explanation
end
end
class ArrayEnumerator
def initialize(array)
@array = array
@index = 0
@mutex = Mutex.new
end
def next
@mutex.synchronize do
@index += 1
return @array[@index - 1]
end
end
end
def select_invalid(hrefs)
enum = ArrayEnumerator.new(hrefs.sort)
mutex = Mutex.new
invalid = Set.new
threads = []
10.times do
threads << Thread.new do
loop do
href = enum.next
break if href.nil?
res = validate(href)
next unless res
mutex.synchronize do
invalid << res
end
end
end
end
threads.each(&:join)
invalid
end
def validate(href)
# Parse
url = nil
begin
url = URI.parse(href)
rescue URI::InvalidURIError
return Result.new(href, 'invalid URI')
end
# Skip excluded URLs
return nil if excluded?(href)
# Skip non-HTTP URLs
return nil if url.scheme !~ /^https?$/
# Get status
res = nil
last_err = nil
timeouts = [3, 5, 10, 30, 60]
5.times do |i|
begin
Timeout.timeout(timeouts[i]) do
res = request_url_once(url)
if res.code == '405'
res = request_url_once(url, Net::HTTP::Get)
end
end
rescue => e
last_err = e
next # can not allow
end
if res.code =~ /^3..$/
if i == 4
return Result.new(href, 'too many redirects')
end
# Find proper location
location = res['Location']
if location !~ /^https?:\/\//
base_url = url.dup
base_url.path = (location =~ /^\// ? '' : '/')
base_url.query = nil
base_url.fragment = nil
location = base_url.to_s + location
end
url = URI.parse(location)
elsif res.code == '200'
return nil
else
return Result.new(href, res.code)
end
end
if last_err
return Result.new(href, last_err.message)
else
raise 'should not have gotten here'
end
end
def path_for_url(url)
path =
if url.path.nil? || url.path.empty?
'/'
else
url.path
end
if url.query
path << '?' << url.query
end
path
end
def request_url_once(url, req_method = Net::HTTP::Head)
req = req_method.new(path_for_url(url))
http = Net::HTTP.new(url.host, url.port)
if url.instance_of? URI::HTTPS
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
http.request(req)
end
def excluded?(href)
excludes = @config.fetch(:checks, {}).fetch(:external_links, {}).fetch(:exclude, [])
excludes.any? { |pattern| Regexp.new(pattern).match(href) }
end
def excluded_file?(file)
excludes = @config.fetch(:checks, {}).fetch(:external_links, {}).fetch(:exclude_files, [])
excludes.any? { |pattern| Regexp.new(pattern).match(file) }
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/checks/html.rb0000644000004100000410000000125512665031555022154 0ustar www-datawww-datamodule ::Nanoc::Extra::Checking::Checks
# @api private
class HTML < ::Nanoc::Extra::Checking::Check
identifier :html
def run
require 'w3c_validators'
Dir[@config[:output_dir] + '/**/*.{htm,html}'].each do |filename|
results = ::W3CValidators::MarkupValidator.new.validate_file(filename)
lines = File.readlines(filename)
results.errors.each do |e|
line_num = e.line.to_i - 1
line = lines[line_num]
message = e.message.gsub(%r{\s+}, ' ').strip.sub(/\s+:$/, '')
desc = "line #{line_num + 1}: #{message}: #{line}"
add_issue(desc, subject: filename)
end
end
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/runner.rb0000644000004100000410000000730112665031555021257 0ustar www-datawww-datamodule Nanoc::Extra::Checking
# Runner is reponsible for running issue checks.
#
# @api private
class Runner
CHECKS_FILENAMES = ['Checks', 'Checks.rb', 'checks', 'checks.rb'].freeze
# @param [Nanoc::Int::Site] site The Nanoc site this runner is for
def initialize(site)
@site = site
end
# @return [String] The name of the Checks file
def checks_filename
@_checks_filename ||= CHECKS_FILENAMES.find { |f| File.file?(f) }
end
# @return [Boolean] true if a Checks file exists, false otherwise
def dsl_present?
checks_filename && File.file?(checks_filename)
end
alias has_dsl? dsl_present?
# Lists all available checks on stdout.
#
# @return [void]
def list_checks
load_dsl_if_available
puts 'Available checks:'
puts
puts all_check_classes.map { |i| ' ' + i.identifier.to_s }.sort.join("\n")
end
# Runs all checks.
#
# @return [Boolean] true if successful, false otherwise
def run_all
load_dsl_if_available
run_check_classes(all_check_classes)
end
# Runs the checks marked for deployment.
#
# @return [Boolean] true if successful, false otherwise
def run_for_deploy
require_dsl
return true if dsl.nil?
run_check_classes(check_classes_named(dsl.deploy_checks))
end
# Runs the checks with the given names.
#
# @param [Array] check_class_names The names of the checks
#
# @return [Boolean] true if successful, false otherwise
def run_specific(check_class_names)
load_dsl_if_available
run_check_classes(check_classes_named(check_class_names))
end
def load_dsl_if_available
@dsl_loaded ||= false
unless @dsl_loaded
@dsl =
if dsl_present?
Nanoc::Extra::Checking::DSL.from_file(checks_filename)
else
nil
end
@dsl_loaded = true
end
end
def require_dsl
load_dsl_if_available
if dsl.nil?
raise Nanoc::Int::Errors::GenericTrivial, "No checks defined (no #{CHECKS_FILENAMES.first} file present)"
end
end
def dsl
@dsl
end
def run_check_classes(classes)
issues = run_checks(classes)
print_issues(issues)
issues.empty? ? true : false
end
def all_check_classes
Nanoc::Extra::Checking::Check.all.map(&:last).uniq
end
def check_classes_named(n)
n.map do |a|
klass = Nanoc::Extra::Checking::Check.named(a)
raise Nanoc::Int::Errors::GenericTrivial, "Unknown check: #{a}" if klass.nil?
klass
end
end
def run_checks(classes)
return [] if classes.empty?
# TODO: remove me
@site.compiler.build_reps
checks = []
issues = Set.new
length = classes.map { |c| c.identifier.to_s.length }.max + 18
classes.each do |klass|
print format(" %-#{length}s", "Running check #{klass.identifier}… ")
check = klass.create(@site)
check.run
checks << check
issues.merge(check.issues)
# TODO: report progress
puts check.issues.empty? ? 'ok'.green : 'error'.red
end
issues
end
def subject_to_s(s)
s || '(global)'
end
def print_issues(issues)
require 'colored'
return if issues.empty?
puts 'Issues found!'
issues.group_by(&:subject).to_a.sort_by { |s| subject_to_s(s.first) }.each do |pair|
subject = pair.first
issues = pair.last
next if issues.empty?
puts " #{subject_to_s(subject)}:"
issues.each do |i|
puts " [ #{'ERROR'.red} ] #{i.check_class.identifier} - #{i.description}"
end
end
end
end
end
nanoc-4.1.4/lib/nanoc/extra/checking/checks.rb0000644000004100000410000000240212665031555021203 0ustar www-datawww-data# @api private
module Nanoc::Extra::Checking::Checks
autoload 'CSS', 'nanoc/extra/checking/checks/css'
autoload 'ExternalLinks', 'nanoc/extra/checking/checks/external_links'
autoload 'HTML', 'nanoc/extra/checking/checks/html'
autoload 'InternalLinks', 'nanoc/extra/checking/checks/internal_links'
autoload 'Stale', 'nanoc/extra/checking/checks/stale'
autoload 'MixedContent', 'nanoc/extra/checking/checks/mixed_content'
Nanoc::Extra::Checking::Check.register '::Nanoc::Extra::Checking::Checks::CSS', :css
Nanoc::Extra::Checking::Check.register '::Nanoc::Extra::Checking::Checks::ExternalLinks', :external_links
Nanoc::Extra::Checking::Check.register '::Nanoc::Extra::Checking::Checks::ExternalLinks', :elinks
Nanoc::Extra::Checking::Check.register '::Nanoc::Extra::Checking::Checks::HTML', :html
Nanoc::Extra::Checking::Check.register '::Nanoc::Extra::Checking::Checks::InternalLinks', :internal_links
Nanoc::Extra::Checking::Check.register '::Nanoc::Extra::Checking::Checks::InternalLinks', :ilinks
Nanoc::Extra::Checking::Check.register '::Nanoc::Extra::Checking::Checks::Stale', :stale
Nanoc::Extra::Checking::Check.register '::Nanoc::Extra::Checking::Checks::MixedContent', :mixed_content
end
nanoc-4.1.4/lib/nanoc/extra/checking/dsl.rb0000644000004100000410000000106112665031555020525 0ustar www-datawww-datamodule Nanoc::Extra::Checking
# @api private
class DSL
attr_reader :deploy_checks
def self.from_file(filename)
dsl = new
dsl.instance_eval(File.read(filename), filename)
dsl
end
def initialize
@deploy_checks = []
end
def check(identifier, &block)
klass = Class.new(::Nanoc::Extra::Checking::Check)
klass.send(:define_method, :run, &block)
klass.send(:identifier, identifier)
end
def deploy_check(*identifiers)
identifiers.each { |i| @deploy_checks << i }
end
end
end
nanoc-4.1.4/lib/nanoc/extra/deployers.rb0000644000004100000410000000050512665031555020200 0ustar www-datawww-datamodule Nanoc::Extra
# @api private
module Deployers
autoload 'Fog', 'nanoc/extra/deployers/fog'
autoload 'Rsync', 'nanoc/extra/deployers/rsync'
Nanoc::Extra::Deployer.register '::Nanoc::Extra::Deployers::Fog', :fog
Nanoc::Extra::Deployer.register '::Nanoc::Extra::Deployers::Rsync', :rsync
end
end
nanoc-4.1.4/lib/nanoc/extra/pruner.rb0000644000004100000410000000451312665031555017510 0ustar www-datawww-datamodule Nanoc::Extra
# Responsible for finding and deleting files in the site’s output directory
# that are not managed by Nanoc.
#
# @api private
class Pruner
# @return [Nanoc::Int::Site] The site this pruner belongs to
attr_reader :site
# @param [Nanoc::Int::Site] site The site for which a pruner is created
#
# @param [Boolean] dry_run true if the files to be deleted
# should only be printed instead of actually deleted, false if the files
# should actually be deleted.
#
# @param [Enumerable] exclude
def initialize(site, dry_run: false, exclude: [])
@site = site
@dry_run = dry_run
@exclude = exclude
end
# Prunes all output files not managed by Nanoc.
#
# @return [void]
def run
require 'find'
# Get compiled files
# FIXME: requires #build_reps to have been called
all_raw_paths = site.compiler.reps.flat_map { |r| r.raw_paths.values }
compiled_files = all_raw_paths.flatten.compact.select { |f| File.file?(f) }
# Get present files and dirs
present_files = []
present_dirs = []
Find.find(site.config[:output_dir] + '/') do |f|
present_files << f if File.file?(f)
present_dirs << f if File.directory?(f)
end
# Remove stray files
stray_files = (present_files - compiled_files)
stray_files.each do |f|
next if filename_excluded?(f)
delete_file(f)
end
# Remove empty directories
present_dirs.reverse_each do |dir|
next if Dir.foreach(dir) { |n| break true if n !~ /\A\.\.?\z/ }
next if filename_excluded?(dir)
delete_dir(dir)
end
end
# @param [String] filename The filename to check
#
# @return [Boolean] true if the given file is excluded, false otherwise
def filename_excluded?(filename)
pathname = Pathname.new(filename)
@exclude.any? { |e| pathname.__nanoc_include_component?(e) }
end
protected
def delete_file(file)
if @dry_run
puts file
else
Nanoc::CLI::Logger.instance.file(:high, :delete, file)
FileUtils.rm(file)
end
end
def delete_dir(dir)
if @dry_run
puts dir
else
Nanoc::CLI::Logger.instance.file(:high, :delete, dir)
Dir.rmdir(dir)
end
end
end
end
nanoc-4.1.4/lib/nanoc/extra/filesystem_tools.rb0000644000004100000410000001226212665031555021601 0ustar www-datawww-datamodule Nanoc::Extra
# Contains useful functions for managing the filesystem.
#
# @api private
module FilesystemTools
# Error that is raised when too many symlink indirections are encountered.
class MaxSymlinkDepthExceededError < ::Nanoc::Int::Errors::GenericTrivial
# @return [String] The last filename that was attempted to be
# resolved before giving up
attr_reader :filename
# @param [String] filename The last filename that was attempted to be
# resolved before giving up
def initialize(filename)
@filename = filename
super("Too many indirections while resolving symlinks. I gave up after finding out #{filename} was yet another symlink. Sorry!")
end
end
# Error that is raised when a file of an unknown type is encountered
# (something other than file, directory or link).
class UnsupportedFileTypeError < ::Nanoc::Int::Errors::GenericTrivial
# @return [String] The filename of the file whose type is not supported
attr_reader :filename
# @param [String] filename The filename of the file whose type is not
# supported
def initialize(filename)
@filename = filename
super("The file at #{filename} is of an unsupported type (expected file, directory or link, but it is #{File.ftype(filename)}")
end
end
# Returns all files in the given directory and directories below it,
# following symlinks up to a maximum of `recursion_limit` times.
#
# @param [String] dir_name The name of the directory whose contents to
# fetch
#
# @param [String, Array, nil] extra_files The list of extra patterns
# to extend the file search for files not found by default, example
# "**/.{htaccess,htpasswd}"
#
# @param [Integer] recursion_limit The maximum number of times to
# recurse into a symlink to a directory
#
# @return [Array] A list of file names
#
# @raise [MaxSymlinkDepthExceededError] if too many indirections are
# encountered while resolving symlinks
#
# @raise [UnsupportedFileTypeError] if a file of an unsupported type is
# detected (something other than file, directory or link)
def all_files_in(dir_name, extra_files, recursion_limit = 10)
all_files_and_dirs_in(dir_name, extra_files).map do |fn|
case File.ftype(fn)
when 'link'
if 0 == recursion_limit
raise MaxSymlinkDepthExceededError.new(fn)
else
absolute_target = resolve_symlink(fn)
if File.file?(absolute_target)
fn
else
all_files_in(absolute_target, extra_files, recursion_limit - 1).map do |sfn|
fn + sfn[absolute_target.size..-1]
end
end
end
when 'file'
fn
when 'directory'
nil
else
raise UnsupportedFileTypeError.new(fn)
end
end.compact.flatten
end
module_function :all_files_in
# Returns all files and directories in the given directory and
# directories below it.
#
# @param [String] dir_name The name of the directory whose contents to
# fetch
#
# @param [String, Array, nil] extra_files The list of extra patterns
# to extend the file search for files not found by default, example
# "**/.{htaccess,htpasswd}"
#
# @return [Array] A list of files and directories
#
# @raise [GenericTrivial] when pattern can not be handled
def all_files_and_dirs_in(dir_name, extra_files)
patterns = ["#{dir_name}/**/*"]
case extra_files
when nil
when String
patterns << "#{dir_name}/#{extra_files}"
when Array
patterns.concat(extra_files.map { |extra_file| "#{dir_name}/#{extra_file}" })
else
raise(
Nanoc::Int::Errors::GenericTrivial,
"Do not know how to handle extra_files: #{extra_files.inspect}",
)
end
Dir.glob(patterns)
end
module_function :all_files_and_dirs_in
# Resolves the given symlink into an absolute path.
#
# @param [String] filename The filename of the symlink to resolve
#
# @param [Integer] recursion_limit The maximum number of times to recurse
# into a symlink
#
# @return [String] The absolute resolved filename of the symlink target
#
# @raise [MaxSymlinkDepthExceededError] if too many indirections are
# encountered while resolving symlinks
#
# @raise [UnsupportedFileTypeError] if a file of an unsupported type is
# detected (something other than file, directory or link)
def resolve_symlink(filename, recursion_limit = 5)
target = File.readlink(filename)
absolute_target = File.expand_path(target, File.dirname(filename))
case File.ftype(absolute_target)
when 'link'
if 0 == recursion_limit
raise MaxSymlinkDepthExceededError.new(absolute_target)
else
resolve_symlink(absolute_target, recursion_limit - 1)
end
when 'file', 'directory'
absolute_target
else
raise UnsupportedFileTypeError.new(absolute_target)
end
end
module_function :resolve_symlink
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl/0000755000004100000410000000000012665031555016333 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/rule_dsl/rules_collection.rb0000644000004100000410000000766212665031555022240 0ustar www-datawww-datamodule Nanoc::RuleDSL
# Keeps track of the rules in a site.
#
# @api private
class RulesCollection
# @return [String] the contents of the Rules file
attr_accessor :data
# The hash containing layout-to-filter mapping rules. This hash is
# ordered: iterating over the hash will happen in insertion order.
#
# @return [Hash] The layout-to-filter mapping rules
attr_reader :layout_filter_mapping
# The hash containing preprocessor code blocks that will be executed after
# all data is loaded but before the site is compiled.
#
# @return [Hash] The hash containing the preprocessor code blocks that will
# be executed after all data is loaded but before the site is compiled
attr_accessor :preprocessors
# The hash containing postprocessor code blocks that will be executed after
# all data is loaded and the site is compiled.
#
# @return [Hash] The hash containing the postprocessor code blocks that will
# be executed after all data is loaded and the site is compiled
attr_accessor :postprocessors
def initialize
@item_compilation_rules = []
@item_routing_rules = []
@layout_filter_mapping = {}
@preprocessors = {}
@postprocessors = {}
end
# Add the given rule to the list of item compilation rules.
#
# @param [Nanoc::Int::Rule] rule The item compilation rule to add
#
# @return [void]
def add_item_compilation_rule(rule)
@item_compilation_rules << rule
end
# Add the given rule to the list of item routing rules.
#
# @param [Nanoc::Int::Rule] rule The item routing rule to add
#
# @return [void]
def add_item_routing_rule(rule)
@item_routing_rules << rule
end
# @param [Nanoc::Int::Item] item The item for which the compilation rules
# should be retrieved
#
# @return [Array] The list of item compilation rules for the given item
def item_compilation_rules_for(item)
@item_compilation_rules.select { |r| r.applicable_to?(item) }
end
# Finds the first matching compilation rule for the given item
# representation.
#
# @param [Nanoc::Int::ItemRep] rep The item rep for which to fetch the rule
#
# @return [Nanoc::Int::Rule, nil] The compilation rule for the given item rep,
# or nil if no rules have been found
def compilation_rule_for(rep)
@item_compilation_rules.find do |rule|
rule.applicable_to?(rep.item) && rule.rep_name == rep.name
end
end
# Returns the list of routing rules that can be applied to the given item
# representation. For each snapshot, the first matching rule will be
# returned. The result is a hash containing the corresponding rule for
# each snapshot.
#
# @param [Nanoc::Int::ItemRep] rep The item rep for which to fetch the rules
#
# @return [Hash] The routing rules for the given rep
def routing_rules_for(rep)
rules = {}
@item_routing_rules.each do |rule|
next unless rule.applicable_to?(rep.item)
next if rule.rep_name != rep.name
next if rules.key?(rule.snapshot_name)
rules[rule.snapshot_name] = rule
end
rules
end
# Finds the filter name and arguments to use for the given layout.
#
# @param [Nanoc::Int::Layout] layout The layout for which to fetch the filter.
#
# @return [Array, nil] A tuple containing the filter name and the filter
# arguments for the given layout.
def filter_for_layout(layout)
@layout_filter_mapping.each_pair do |pattern, filter_name_and_args|
return filter_name_and_args if pattern.match?(layout.identifier)
end
nil
end
# Returns an object that can be used for uniquely identifying objects.
#
# @return [Object] An unique reference to this object
def reference
:rules
end
def inspect
"<#{self.class}>"
end
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl/rule.rb0000644000004100000410000000451312665031555017632 0ustar www-datawww-datamodule Nanoc::RuleDSL
# Contains the processing information for a item.
#
# @api private
class Rule
# @return [Symbol] The name of the representation that will be compiled
# using this rule
attr_reader :rep_name
# @return [Symbol] The name of the snapshot this rule will apply to.
# Ignored for compilation rules, but used for routing rules.
#
# @since 3.2.0
attr_reader :snapshot_name
attr_reader :pattern
# Creates a new item compilation rule with the given identifier regex,
# compiler and block. The block will be called during compilation with the
# item rep as its argument.
#
# @param [Nanoc::Int::Pattern] pattern
#
# @param [String, Symbol] rep_name The name of the item representation
# where this rule can be applied to
#
# @param [Proc] block A block that will be called when matching items are
# compiled
#
# @param [Symbol, nil] snapshot_name The name of the snapshot this rule will
# apply to. Ignored for compilation rules, but used for routing rules.
def initialize(pattern, rep_name, block, snapshot_name: nil)
@pattern = pattern
@rep_name = rep_name.to_sym
@snapshot_name = snapshot_name
@block = block
end
# @param [Nanoc::Int::Item] item The item to check
#
# @return [Boolean] true if this rule can be applied to the given item
# rep, false otherwise
def applicable_to?(item)
@pattern.match?(item.identifier)
end
# Applies this rule to the given item rep.
#
# @param [Nanoc::Int::ItemRep] rep
# @param [Nanoc::Int::Site] site
# @param [Nanoc::Int::Executor, Nanoc::RuleDSL::RecordingExecutor] executor
# @param [Nanoc::ViewContext] view_context
#
# @return [void]
def apply_to(rep, site:, executor:, view_context:)
context = Nanoc::RuleDSL::RuleContext.new(
rep: rep, executor: executor, site: site, view_context: view_context)
context.instance_exec(matches(rep.item.identifier), &@block)
end
protected
# Matches the rule regexp against items identifier and gives back group
# captures if any
#
# @param [String] identifier Identifier to capture groups for
#
# @return [nil, Array] Captured groups, if any
def matches(identifier)
@pattern.captures(identifier)
end
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl/compiler_dsl.rb0000644000004100000410000002525712665031555021347 0ustar www-datawww-datamodule Nanoc::RuleDSL
# Contains methods that will be executed by the site’s `Rules` file.
#
# @api private
class CompilerDSL
# The current rules filename.
#
# @return [String] The current rules filename.
#
# @api private
attr_accessor :rules_filename
# Creates a new compiler DSL for the given collection of rules.
#
# @api private
#
# @param [Nanoc::RuleDSL::RulesCollection] rules_collection The collection of
# rules to modify when loading this DSL
#
# @param [Hash] config The site configuration
def initialize(rules_collection, config)
@rules_collection = rules_collection
@config = config
end
# Creates a preprocessor block that will be executed after all data is
# loaded, but before the site is compiled.
#
# @yield The block that will be executed before site compilation starts
#
# @return [void]
def preprocess(&block)
if @rules_collection.preprocessors[rules_filename]
warn 'WARNING: A preprocess block is already defined. Defining ' \
'another preprocess block overrides the previously one.'
end
@rules_collection.preprocessors[rules_filename] = block
end
# Creates a compilation rule for all items whose identifier match the
# given identifier, which may either be a string containing the *
# wildcard, or a regular expression.
#
# This rule will be applicable to reps with a name equal to `:default`;
# this can be changed by giving an explicit `:rep` parameter.
#
# An item rep will be compiled by calling the given block and passing the
# rep as a block argument.
#
# @param [String] identifier A pattern matching identifiers of items that
# should be compiled using this rule
#
# @param [Symbol] rep The name of the representation
#
# @yield The block that will be executed when an item matching this
# compilation rule needs to be compiled
#
# @return [void]
#
# @example Compiling the default rep of the `/foo/` item
#
# compile '/foo/' do
# rep.filter :erb
# end
#
# @example Compiling the `:raw` rep of the `/bar/` item
#
# compile '/bar/', :rep => :raw do
# # do nothing
# end
def compile(identifier, rep: :default, &block)
raise ArgumentError.new('#compile requires a block') unless block_given?
rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, block)
@rules_collection.add_item_compilation_rule(rule)
end
# Creates a routing rule for all items whose identifier match the
# given identifier, which may either be a string containing the `*`
# wildcard, or a regular expression.
#
# This rule will be applicable to reps with a name equal to `:default`;
# this can be changed by giving an explicit `:rep` parameter.
#
# The path of an item rep will be determined by calling the given block
# and passing the rep as a block argument.
#
# @param [String] identifier A pattern matching identifiers of items that
# should be routed using this rule
#
# @param [Symbol] rep The name of the representation
#
# @param [Symbol] snapshot The name of the snapshot
#
# @yield The block that will be executed when an item matching this
# compilation rule needs to be routed
#
# @return [void]
#
# @example Routing the default rep of the `/foo/` item
#
# route '/foo/' do
# item.identifier + 'index.html'
# end
#
# @example Routing the `:raw` rep of the `/bar/` item
#
# route '/bar/', :rep => :raw do
# '/raw' + item.identifier + 'index.txt'
# end
def route(identifier, rep: :default, snapshot: :last, &block)
raise ArgumentError.new('#route requires a block') unless block_given?
rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, block, snapshot_name: snapshot)
@rules_collection.add_item_routing_rule(rule)
end
# Creates a layout rule for all layouts whose identifier match the given
# identifier, which may either be a string containing the * wildcard, or a
# regular expression. The layouts matching the identifier will be filtered
# using the filter specified in the second argument. The params hash
# contains filter arguments that will be passed to the filter.
#
# @param [String] identifier A pattern matching identifiers of layouts
# that should be filtered using this rule
#
# @param [Symbol] filter_name The name of the filter that should be run
# when processing the layout
#
# @param [Hash] params Extra filter arguments that should be passed to the
# filter when processing the layout (see {Nanoc::Filter#run})
#
# @return [void]
#
# @example Specifying the filter to use for a layout
#
# layout '/default/', :erb
#
# @example Using custom filter arguments for a layout
#
# layout '/custom/', :haml, :format => :html5
def layout(identifier, filter_name, params = {})
pattern = Nanoc::Int::Pattern.from(create_pattern(identifier))
@rules_collection.layout_filter_mapping[pattern] = [filter_name, params]
end
# Creates a pair of compilation and routing rules that indicate that the
# specified item(s) should be copied to the output folder as-is. The items
# are selected using an identifier, which may either be a string
# containing the `*` wildcard, or a regular expression.
#
# This meta-rule will be applicable to reps with a name equal to
# `:default`; this can be changed by giving an explicit `:rep` parameter.
#
# @param [String] identifier A pattern matching identifiers of items that
# should be processed using this meta-rule
#
# @param [Symbol] rep The name of the representation
#
# @return [void]
#
# @since 3.2.0
#
# @example Copying the `/foo/` item as-is
#
# passthrough '/foo/'
#
# @example Copying the `:raw` rep of the `/bar/` item as-is
#
# passthrough '/bar/', :rep => :raw
def passthrough(identifier, rep: :default)
raise ArgumentError.new('#passthrough does not require a block') if block_given?
compilation_block = proc {}
compilation_rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, compilation_block)
@rules_collection.add_item_compilation_rule(compilation_rule)
# Create routing rule
routing_block = proc do
if item.identifier.full?
item.identifier.to_s
else
# This is a temporary solution until an item can map back to its data
# source.
# ATM item[:content_filename] is nil for items coming from the static
# data source.
item[:extension].nil? || (item[:content_filename].nil? && item.identifier =~ %r{#{item[:extension]}/$}) ? item.identifier.chop : item.identifier.chop + '.' + item[:extension]
end
end
routing_rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, routing_block, snapshot_name: :last)
@rules_collection.add_item_routing_rule(routing_rule)
end
# Creates a pair of compilation and routing rules that indicate that the
# specified item(s) should be ignored, e.g. compiled and routed with an
# empty rule. The items are selected using an identifier, which may either
# be a string containing the `*` wildcard, or a regular expression.
#
# This meta-rule will be applicable to reps with a name equal to
# `:default`; this can be changed by giving an explicit `:rep` parameter.
#
# @param [String] identifier A pattern matching identifiers of items that
# should be processed using this meta-rule
#
# @param [Symbol] rep The name of the representation
#
# @return [void]
#
# @example Suppressing compilation and output for all all `/foo/*` items.
#
# ignore '/foo/*'
def ignore(identifier, rep: :default)
raise ArgumentError.new('#ignore does not require a block') if block_given?
compilation_rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, proc {})
@rules_collection.add_item_compilation_rule(compilation_rule)
routing_rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, proc {}, snapshot_name: :last)
@rules_collection.add_item_routing_rule(routing_rule)
end
# Includes an additional rules file in the current rules collection.
#
# @param [String] name The name of the rules file — an ".rb" extension is
# implied if not explicitly given
#
# @return [void]
#
# @example Including two additional rules files, 'rules/assets.rb' and
# 'rules/content.rb'
#
# include_rules 'rules/assets'
# include_rules 'rules/content'
def include_rules(name)
filename = [name.to_s, "#{name}.rb", "./#{name}", "./#{name}.rb"].find { |f| File.file?(f) }
raise Nanoc::Int::Errors::NoRulesFileFound.new if filename.nil?
Nanoc::RuleDSL::RulesLoader.new(@config, @rules_collection).parse(filename)
end
# Creates a postprocessor block that will be executed after all data is
# loaded and the site is compiled.
#
# @yield The block that will be executed after site compilation completes
#
# @return [void]
def postprocess(&block)
if @rules_collection.postprocessors[rules_filename]
warn 'WARNING: A postprocess block is already defined. Defining ' \
'another postprocess block overrides the previously one.'
end
@rules_collection.postprocessors[rules_filename] = block
end
# @api private
def create_pattern(arg)
case @config[:string_pattern_type]
when 'glob'
Nanoc::Int::Pattern.from(arg)
when 'legacy'
Nanoc::Int::Pattern.from(identifier_to_regex(arg))
else
raise(
Nanoc::Int::Errors::GenericTrivial,
"Invalid string_pattern_type: #{@config[:string_pattern_type]}",
)
end
end
private
# Converts the given identifier, which can contain the '*' or '+'
# wildcard characters, matching zero or more resp. one or more
# characters, to a regex. For example, 'foo/*/bar' is transformed
# into /^foo\/(.*?)\/bar$/ and 'foo+' is transformed into /^foo(.+?)/.
def identifier_to_regex(identifier)
if identifier.is_a? String
# Add leading/trailing slashes if necessary
new_identifier = identifier.dup
new_identifier[/^/] = '/' if identifier[0, 1] != '/'
new_identifier[/$/] = '/' unless ['*', '/'].include?(identifier[-1, 1])
/^#{new_identifier.gsub('*', '(.*?)').gsub('+', '(.+?)')}$/
else
identifier
end
end
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl/rules_loader.rb0000644000004100000410000000146512665031555021346 0ustar www-datawww-datamodule Nanoc::RuleDSL
# @api private
class RulesLoader
def initialize(config, rules_collection)
@dsl = Nanoc::RuleDSL::CompilerDSL.new(rules_collection, config)
end
def load
# Find rules file
rules_filenames = ['Rules', 'rules', 'Rules.rb', 'rules.rb']
rules_filename = rules_filenames.find { |f| File.file?(f) }
raise Nanoc::Int::Errors::NoRulesFileFound.new if rules_filename.nil?
parse(rules_filename)
end
def parse(rules_filename)
rules_filename = File.absolute_path(rules_filename)
# Get rule data
data = File.read(rules_filename)
old_rules_filename = @dsl.rules_filename
@dsl.rules_filename = rules_filename
@dsl.instance_eval(data, rules_filename)
@dsl.rules_filename = old_rules_filename
end
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl/action_provider.rb0000644000004100000410000000426212665031555022053 0ustar www-datawww-datamodule Nanoc::RuleDSL
class ActionProvider < Nanoc::Int::ActionProvider
identifier :rule_dsl
# @api private
attr_reader :rules_collection
def self.for(site)
rules_collection = Nanoc::RuleDSL::RulesCollection.new
rule_memory_calculator =
Nanoc::RuleDSL::RuleMemoryCalculator.new(
rules_collection: rules_collection, site: site)
action_provider = new(rules_collection, rule_memory_calculator)
Nanoc::RuleDSL::RulesLoader.new(site.config, rules_collection).load
action_provider
end
def initialize(rules_collection, rule_memory_calculator)
@rules_collection = rules_collection
@rule_memory_calculator = rule_memory_calculator
end
def rep_names_for(item)
matching_rules = @rules_collection.item_compilation_rules_for(item)
raise Nanoc::Int::Errors::NoMatchingCompilationRuleFound.new(item) if matching_rules.empty?
matching_rules.map(&:rep_name).uniq
end
def memory_for(rep)
@rule_memory_calculator[rep]
end
def snapshots_defs_for(rep)
@rule_memory_calculator.snapshots_defs_for(rep)
end
def preprocess(site)
ctx = new_preprocessor_context(site)
@rules_collection.preprocessors.each_value do |preprocessor|
ctx.instance_eval(&preprocessor)
end
end
def postprocess(site, reps)
view_context = Nanoc::ViewContext.new(reps: reps, items: site.items)
ctx = new_postprocessor_context(site, view_context)
@rules_collection.postprocessors.each_value do |postprocessor|
ctx.instance_eval(&postprocessor)
end
end
# @api private
def new_preprocessor_context(site)
Nanoc::Int::Context.new(
config: Nanoc::MutableConfigView.new(site.config, nil),
items: Nanoc::MutableItemCollectionView.new(site.items, nil),
layouts: Nanoc::MutableLayoutCollectionView.new(site.layouts, nil),
)
end
# @api private
def new_postprocessor_context(site, view_context)
Nanoc::Int::Context.new(
config: Nanoc::ConfigView.new(site.config, view_context),
items: Nanoc::PostCompileItemCollectionView.new(site.items, view_context),
)
end
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl/rule_context.rb0000644000004100000410000000534712665031555021404 0ustar www-datawww-datamodule Nanoc::RuleDSL
# Provides a context in which compilation and routing rules can be executed.
# It provides access to the item representation that is being compiled or
# routed.
#
# @api private
class RuleContext < Nanoc::Int::Context
# @param [Nanoc::Int::ItemRep] rep
# @param [Nanoc::Int::Site] site
# @param [Nanoc::Int::Executor, Nanoc::RuleDSL::RecordingExecutor] executor
# @param [Nanoc::ViewContext] view_context
def initialize(rep:, site:, executor:, view_context:)
@_executor = executor
super({
item: Nanoc::ItemWithoutRepsView.new(rep.item, view_context),
rep: Nanoc::ItemRepView.new(rep, view_context),
item_rep: Nanoc::ItemRepView.new(rep, view_context),
items: Nanoc::ItemCollectionWithoutRepsView.new(site.items, view_context),
layouts: Nanoc::LayoutCollectionView.new(site.layouts, view_context),
config: Nanoc::ConfigView.new(site.config, view_context),
site: Nanoc::SiteView.new(site, view_context),
})
end
# Filters the current representation (calls {Nanoc::Int::ItemRep#filter} with
# the given arguments on the rep).
#
# @see Nanoc::Int::ItemRep#filter
#
# @param [Symbol] filter_name The name of the filter to run the item
# representations' content through
#
# @param [Hash] filter_args The filter arguments that should be passed to
# the filter's #run method
#
# @return [void]
def filter(filter_name, filter_args = {})
@_executor.filter(rep.unwrap, filter_name, filter_args)
end
# Layouts the current representation (calls {Nanoc::Int::ItemRep#layout} with
# the given arguments on the rep).
#
# @see Nanoc::Int::ItemRep#layout
#
# @param [String] layout_identifier The identifier of the layout the item
# should be laid out with
#
# @return [void]
def layout(layout_identifier, extra_filter_args = nil)
@_executor.layout(rep.unwrap, layout_identifier, extra_filter_args)
end
# Creates a snapshot of the current compiled item content. Calls
# {Nanoc::Int::ItemRep#snapshot} with the given arguments on the rep.
#
# @see Nanoc::Int::ItemRep#snapshot
#
# @param [Symbol] snapshot_name The name of the snapshot to create
#
# @param [String, nil] path
#
# @return [void]
def snapshot(snapshot_name, path: nil)
@_executor.snapshot(rep.unwrap, snapshot_name, path: path)
end
# Creates a snapshot named :last the current compiled item content, with
# the given path. This is a convenience method for {#snapshot}.
#
# @see #snapshot
#
# @param [String] path
#
# @return [void]
def write(path)
snapshot(:last, path: path)
end
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl/recording_executor.rb0000644000004100000410000000331512665031555022554 0ustar www-datawww-datamodule Nanoc
module RuleDSL
class RecordingExecutor
class PathWithoutInitialSlashError < ::Nanoc::Error
def initialize(rep, basic_path)
super("The path returned for the #{rep.inspect} item representation, “#{basic_path}”, does not start with a slash. Please ensure that all routing rules return a path that starts with a slash.")
end
end
attr_reader :rule_memory
def initialize(item_rep, rules_collection, site)
@item_rep = item_rep
@rules_collection = rules_collection
@site = site
@rule_memory = Nanoc::Int::RuleMemory.new(item_rep)
end
def filter(_rep, filter_name, filter_args = {})
@rule_memory.add_filter(filter_name, filter_args)
end
def layout(_rep, layout_identifier, extra_filter_args = {})
unless @rule_memory.any_layouts?
@rule_memory.add_snapshot(:pre, true, nil)
end
@rule_memory.add_layout(layout_identifier, extra_filter_args)
end
def snapshot(rep, snapshot_name, final: true, path: nil)
actual_path = final ? (path || basic_path_from_rules_for(rep, snapshot_name)) : nil
@rule_memory.add_snapshot(snapshot_name, final, actual_path)
end
def basic_path_from_rules_for(rep, snapshot_name)
routing_rules = @rules_collection.routing_rules_for(rep)
routing_rule = routing_rules[snapshot_name]
return nil if routing_rule.nil?
basic_path = routing_rule.apply_to(rep, executor: nil, site: @site, view_context: nil)
if basic_path && !basic_path.start_with?('/')
raise PathWithoutInitialSlashError.new(rep, basic_path)
end
basic_path
end
end
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl/rule_memory_calculator.rb0000644000004100000410000000527112665031555023435 0ustar www-datawww-datamodule Nanoc::RuleDSL
# Calculates rule memories for objects that can be run through a rule (item
# representations and layouts).
#
# @api private
class RuleMemoryCalculator
extend Nanoc::Int::Memoization
class UnsupportedObjectTypeException < ::Nanoc::Error
def initialize(obj)
super("Do not know how to calculate the rule memory for #{obj.inspect}")
end
end
# @api private
attr_accessor :rules_collection
# @param [Nanoc::Int::Site] site
# @param [Nanoc::RuleDSL::RulesCollection] rules_collection
def initialize(site:, rules_collection:)
@site = site
@rules_collection = rules_collection
end
# @param [#reference] obj
#
# @return [Nanoc::Int::RuleMemory]
def [](obj)
# FIXME: Remove this
obj = obj.unwrap if obj.respond_to?(:unwrap)
case obj
when Nanoc::Int::ItemRep
new_rule_memory_for_rep(obj)
when Nanoc::Int::Layout
new_rule_memory_for_layout(obj)
else
raise UnsupportedObjectTypeException.new(obj)
end
end
memoize :[]
# @param [Nanoc::Int::ItemRep] rep The item representation for which to fetch
# the list of snapshots
#
# @return [Array] A list of snapshots, represented as arrays where the
# first element is the snapshot name (a Symbol) and the last element is
# a Boolean indicating whether the snapshot is final or not
def snapshots_defs_for(rep)
self[rep].snapshot_actions.map do |a|
Nanoc::Int::SnapshotDef.new(a.snapshot_name, a.final?)
end
end
# @param [Nanoc::Int::ItemRep] rep The item representation to get the rule
# memory for
#
# @return [Nanoc::Int::RuleMemory]
def new_rule_memory_for_rep(rep)
# FIXME: What if #compilation_rule_for returns nil?
executor = Nanoc::RuleDSL::RecordingExecutor.new(rep, @rules_collection, @site)
rule = @rules_collection.compilation_rule_for(rep)
executor.snapshot(rep, :raw)
executor.snapshot(rep, :pre, final: false)
rule.apply_to(rep, executor: executor, site: @site, view_context: nil)
if executor.rule_memory.any_layouts?
executor.snapshot(rep, :post)
end
unless executor.rule_memory.snapshot_actions.any? { |sa| sa.snapshot_name == :last }
executor.snapshot(rep, :last)
end
executor.rule_memory
end
# @param [Nanoc::Int::Layout] layout
#
# @return [Nanoc::Int::RuleMemory]
def new_rule_memory_for_layout(layout)
res = @rules_collection.filter_for_layout(layout)
# FIXME: what if res is nil?
Nanoc::Int::RuleMemory.new(layout).tap do |rm|
rm.add_filter(res[0], res[1])
end
end
end
end
nanoc-4.1.4/lib/nanoc/cli/0000755000004100000410000000000012665031555015271 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/cli/ansi_string_colorizer.rb0000644000004100000410000000125212665031555022226 0ustar www-datawww-datamodule Nanoc::CLI
# A simple ANSI colorizer for strings. When given a string and a list of
# attributes, it returns a colorized string.
#
# @api private
module ANSIStringColorizer
# TODO: complete mapping
MAPPING = {
bold: "\e[1m",
red: "\e[31m",
green: "\e[32m",
yellow: "\e[33m",
blue: "\e[34m",
}.freeze
# @param [String] s The string to colorize
#
# @param [Array] as An array of attributes from `MAPPING` to colorize the
# string with
#
# @return [String] A string colorized using the given attributes
def self.c(s, *as)
as.map { |a| MAPPING[a] }.join('') + s + "\e[0m"
end
end
end
nanoc-4.1.4/lib/nanoc/cli/commands/0000755000004100000410000000000012665031555017072 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/cli/commands/check.rb0000644000004100000410000000247312665031555020502 0ustar www-datawww-datausage 'check [options] [names]'
summary 'run issue checks'
description "
Run issue checks on the current site. If the `--all` option is passed, all available issue checks will be run. If the `--deploy` option is passed, the issue checks marked for deployment will be run.
"
flag :a, :all, 'run all checks'
flag :L, :list, 'list all checks'
flag :d, :deploy, 'run checks for deployment'
module Nanoc::CLI::Commands
class Check < ::Nanoc::CLI::CommandRunner
def run
validate_options_and_arguments
load_site
runner = Nanoc::Extra::Checking::Runner.new(site)
if options[:list]
runner.list_checks
return
end
success =
if options[:all]
runner.run_all
elsif options[:deploy]
runner.run_for_deploy
else
runner.run_specific(arguments)
end
unless success
raise Nanoc::Int::Errors::GenericTrivial, 'One or more checks failed'
end
end
protected
def validate_options_and_arguments
if arguments.empty? && !options[:all] && !options[:deploy] && !options[:list]
raise(
Nanoc::Int::Errors::GenericTrivial,
'nothing to do (pass either --all, --deploy or --list or a list of checks)',
)
end
end
end
end
runner Nanoc::CLI::Commands::Check
nanoc-4.1.4/lib/nanoc/cli/commands/shell.rb0000644000004100000410000000131212665031555020523 0ustar www-datawww-datausage 'shell'
summary 'open a shell on the Nanoc environment'
aliases 'console'
description "
Open an IRB shell on a context that contains @items, @layouts, and @config.
"
module Nanoc::CLI::Commands
class Shell < ::Nanoc::CLI::CommandRunner
def run
require 'pry'
load_site
Nanoc::Int::Context.new(env).pry
end
protected
def env
self.class.env_for_site(site)
end
def self.env_for_site(site)
{
items: Nanoc::ItemCollectionWithRepsView.new(site.items, nil),
layouts: Nanoc::LayoutCollectionView.new(site.layouts, nil),
config: Nanoc::ConfigView.new(site.config, nil),
}
end
end
end
runner Nanoc::CLI::Commands::Shell
nanoc-4.1.4/lib/nanoc/cli/commands/view.rb0000644000004100000410000000401412665031555020370 0ustar www-datawww-datausage 'view [options]'
summary 'start the web server that serves static files'
description <<-EOS
Start the static web server. Unless specified, the web server will run on port
3000 and listen on all IP addresses. Running this static web server requires
`adsf` (not `asdf`!).
EOS
required :H, :handler, 'specify the handler to use (webrick/mongrel/...)'
required :o, :host, 'specify the host to listen on (default: 0.0.0.0)'
required :p, :port, 'specify the port to listen on (default: 3000)'
module Nanoc::CLI::Commands
class View < ::Nanoc::CLI::CommandRunner
DEFAULT_HANDLER_NAME = :thin
def run
load_adsf
require 'rack'
load_site
# Set options
options_for_rack = {
Port: (options[:port] || 3000).to_i,
Host: (options[:host] || '0.0.0.0'),
}
# Get handler
if options.key?(:handler)
handler = Rack::Handler.get(options[:handler])
else
begin
handler = Rack::Handler.get(DEFAULT_HANDLER_NAME)
rescue LoadError
handler = Rack::Handler::WEBrick
end
end
# Build app
site = self.site
app = Rack::Builder.new do
use Rack::CommonLogger
use Rack::ShowExceptions
use Rack::Lint
use Rack::Head
use Adsf::Rack::IndexFileFinder, root: site.config[:output_dir]
run Rack::File.new(site.config[:output_dir])
end.to_app
# Run autocompiler
handler.run(app, options_for_rack)
end
protected
def load_adsf
# Load adsf
begin
require 'adsf'
return
rescue LoadError
$stderr.puts "Could not find the required 'adsf' gem, " \
'which is necessary for the view command.'
end
# Check asdf
begin
require 'asdf'
$stderr.puts "You appear to have 'asdf' installed, " \
"but not 'adsf'. Please install 'adsf' (check the spelling)!"
rescue LoadError
end
# Done
exit 1
end
end
end
runner Nanoc::CLI::Commands::View
nanoc-4.1.4/lib/nanoc/cli/commands/create-site.rb0000644000004100000410000002403312665031555021626 0ustar www-datawww-datausage 'create-site [options] path'
aliases :create_site, :cs
summary 'create a site'
description 'Create a new site at the given path. The site will use the `filesystem` data source.'
flag nil, :force, 'Force creation of new site. Disregards previous existence of site in destination'
module Nanoc::CLI::Commands
class CreateSite < ::Nanoc::CLI::CommandRunner
class << self
protected
# Converts the given array to YAML format
def array_to_yaml(array)
'[ ' + array.map { |s| "'" + s + "'" }.join(', ') + ' ]'
end
end
DEFAULT_CONFIG = <A Brand New Nanoc Site
You’ve just created a new Nanoc site. The page you are looking at right now is the home page for your site. To get started, consider replacing this default homepage with your own customized homepage. Some pointers on how to do so:
Change this page’s content by editing the “index.html” file in the “content” directory. This is the actual page content, and therefore doesn’t include the header, sidebar or style information (those are part of the layout).
Change the layout, which is the “default.html” file in the “layouts” directory, and create something unique (and hopefully less bland).
If you need any help with customizing your Nanoc web site, be sure to check out the documentation (see sidebar), and be sure to subscribe to the discussion group (also see sidebar). Enjoy!
EOS
DEFAULT_STYLESHEET = <
A Brand New Nanoc Site - <%= @item[:title] %>
<%= yield %>
EOS
def run
# Extract arguments
if arguments.length != 1
raise Nanoc::Int::Errors::GenericTrivial, "usage: #{command.usage}"
end
path = arguments[0]
# Check whether site exists
if File.exist?(path) && (!File.directory?(path) || !(Dir.entries(path) - %w(. ..)).empty?) && !options[:force]
raise(
Nanoc::Int::Errors::GenericTrivial,
"The site was not created because '#{path}' already exists. " \
'Re-run the command using --force to create the site anyway.',
)
end
# Setup notifications
Nanoc::Int::NotificationCenter.on(:file_created) do |file_path|
Nanoc::CLI::Logger.instance.file(:high, :create, file_path)
end
# Build entire site
FileUtils.mkdir_p(path)
FileUtils.cd(File.join(path)) do
FileUtils.mkdir_p('content')
FileUtils.mkdir_p('layouts')
FileUtils.mkdir_p('lib')
FileUtils.mkdir_p('output')
write('nanoc.yaml', DEFAULT_CONFIG)
write('Rules', DEFAULT_RULES)
write('content/index.html', DEFAULT_ITEM)
write('content/stylesheet.css', DEFAULT_STYLESHEET)
write('layouts/default.html', DEFAULT_LAYOUT)
end
puts "Created a blank nanoc site at '#{path}'. Enjoy!"
end
private
def write(filename, content)
File.write(filename, content)
Nanoc::Int::NotificationCenter.post(:file_created, filename)
end
end
end
runner Nanoc::CLI::Commands::CreateSite
nanoc-4.1.4/lib/nanoc/cli/commands/compile.rb0000644000004100000410000003063212665031555021053 0ustar www-datawww-datausage 'compile [options]'
summary 'compile items of this site'
description <<-EOS
Compile all items of the current site.
The compile command will show all items of the site as they are processed. The time spent compiling the item will be printed, as well as a status message, which can be one of the following:
CREATED - The compiled item did not yet exist and has been created
UPDATED - The compiled item did already exist and has been modified
IDENTICAL - The item was deemed outdated and has been recompiled, but the compiled version turned out to be identical to the already existing version
SKIP - The item was deemed not outdated and was therefore not recompiled
EOS
module Nanoc::CLI::Commands
class Compile < ::Nanoc::CLI::CommandRunner
# Listens to compilation events and reacts to them. This abstract class
# does not have a real implementation; subclasses should override {#start}
# and set up notifications to listen to.
#
# @abstract Subclasses must override {#start} and may override {#stop}.
class Listener
def initialize(*)
end
# @param [Nanoc::CLI::CommandRunner] command_runner The command runner for this listener
#
# @return [Boolean] true if this listener should be enabled for the given command runner, false otherwise
#
# @abstract Returns `true` by default, but subclasses may override this.
def self.enable_for?(command_runner) # rubocop:disable Lint/UnusedMethodArgument
true
end
# Starts the listener. Subclasses should override this method and set up listener notifications.
#
# @return [void]
#
# @abstract
def start
raise NotImplementedError, 'Subclasses of Listener should implement #start'
end
# Stops the listener. The default implementation removes self from all notification center observers.
#
# @return [void]
def stop
end
end
# Generates diffs for every output file written
class DiffGenerator < Listener
# @see Listener#enable_for?
def self.enable_for?(command_runner)
command_runner.site.config[:enable_output_diff]
end
# @see Listener#start
def start
require 'tempfile'
setup_diffs
old_contents = {}
Nanoc::Int::NotificationCenter.on(:will_write_rep) do |rep, path|
old_contents[rep] = File.file?(path) ? File.read(path) : nil
end
Nanoc::Int::NotificationCenter.on(:rep_written) do |rep, path, _is_created, _is_modified|
unless rep.binary?
new_contents = File.file?(path) ? File.read(path) : nil
if old_contents[rep] && new_contents
generate_diff_for(path, old_contents[rep], new_contents)
end
old_contents.delete(rep)
end
end
end
# @see Listener#stop
def stop
super
teardown_diffs
end
protected
def setup_diffs
@diff_lock = Mutex.new
@diff_threads = []
FileUtils.rm('output.diff') if File.file?('output.diff')
end
def teardown_diffs
@diff_threads.each(&:join)
end
def generate_diff_for(path, old_content, new_content)
return if old_content == new_content
@diff_threads << Thread.new do
# Generate diff
diff = diff_strings(old_content, new_content)
diff.sub!(/^--- .*/, '--- ' + path)
diff.sub!(/^\+\+\+ .*/, '+++ ' + path)
# Write diff
@diff_lock.synchronize do
File.open('output.diff', 'a') { |io| io.write(diff) }
end
end
end
def diff_strings(a, b)
require 'open3'
# Create files
Tempfile.open('old') do |old_file|
Tempfile.open('new') do |new_file|
# Write files
old_file.write(a)
old_file.flush
new_file.write(b)
new_file.flush
# Diff
cmd = ['diff', '-u', old_file.path, new_file.path]
Open3.popen3(*cmd) do |_stdin, stdout, _stderr|
result = stdout.read
return (result == '' ? nil : result)
end
end
end
rescue Errno::ENOENT
warn 'Failed to run `diff`, so no diff with the previously compiled ' \
'content will be available.'
nil
end
end
# Records the time spent per filter and per item representation
class TimingRecorder < Listener
# @see Listener#enable_for?
def self.enable_for?(command_runner)
command_runner.options.fetch(:verbose, false)
end
# @param [Enumerable] reps
def initialize(reps:)
@times = {}
@reps = reps
end
# @see Listener#start
def start
Nanoc::Int::NotificationCenter.on(:filtering_started) do |_rep, filter_name|
@times[filter_name] ||= []
@times[filter_name] << { start: Time.now }
end
Nanoc::Int::NotificationCenter.on(:filtering_ended) do |_rep, filter_name|
@times[filter_name].last[:stop] = Time.now
end
end
# @see Listener#stop
def stop
print_profiling_feedback
super
end
protected
def print_profiling_feedback
# Get max filter length
max_filter_name_length = durations_per_filter.keys.map { |k| k.to_s.size }.max
return if max_filter_name_length.nil?
# Print warning if necessary
if @reps.any? { |r| !r.compiled? }
$stderr.puts
$stderr.puts 'Warning: profiling information may not be accurate because ' \
'some items were not compiled.'
end
# Print header
puts
puts ' ' * max_filter_name_length + ' | count min avg max tot'
puts '-' * max_filter_name_length + '-+-----------------------------------'
durations_per_filter.to_a.sort_by { |r| r[1] }.each do |row|
print_row(row, max_filter_name_length)
end
end
def print_row(row, length)
# Extract data
filter_name, samples = *row
# Calculate stats
count = samples.size
min = samples.min
tot = samples.reduce(0) { |a, e| a + e }
avg = tot / count
max = samples.max
# Format stats
count = format('%4d', count)
min = format('%4.2f', min)
avg = format('%4.2f', avg)
max = format('%4.2f', max)
tot = format('%5.2f', tot)
# Output stats
key = format("%#{length}s", filter_name)
puts "#{key} | #{count} #{min}s #{avg}s #{max}s #{tot}s"
end
def durations_per_filter
@_durations_per_filter ||= begin
result = {}
@times.keys.each do |filter_name|
durations = durations_for_filter(filter_name)
if durations
result[filter_name] = durations
end
end
result
end
end
def durations_for_filter(filter_name)
result = []
@times[filter_name].each do |sample|
if sample[:start] && sample[:stop]
result << sample[:stop] - sample[:start]
end
end
result
end
end
# Controls garbage collection so that it only occurs once every 20 items
class GCController < Listener
# @see Listener#enable_for?
def self.enable_for?(_command_runner)
!ENV.key?('TRAVIS')
end
def initialize(*)
@gc_count = 0
end
# @see Listener#start
def start
Nanoc::Int::NotificationCenter.on(:compilation_started) do |_rep|
if @gc_count % 20 == 0
GC.enable
GC.start
GC.disable
end
@gc_count += 1
end
end
# @see Listener#stop
def stop
super
GC.enable
end
end
# Prints debug information (compilation started/ended, filtering started/ended, …)
class DebugPrinter < Listener
# @see Listener#enable_for?
def self.enable_for?(command_runner)
command_runner.debug?
end
# @see Listener#start
def start
Nanoc::Int::NotificationCenter.on(:compilation_started) do |rep|
puts "*** Started compilation of #{rep.inspect}"
end
Nanoc::Int::NotificationCenter.on(:compilation_ended) do |rep|
puts "*** Ended compilation of #{rep.inspect}"
puts
end
Nanoc::Int::NotificationCenter.on(:compilation_failed) do |rep, e|
puts "*** Suspended compilation of #{rep.inspect}: #{e.message}"
end
Nanoc::Int::NotificationCenter.on(:cached_content_used) do |rep|
puts "*** Used cached compiled content for #{rep.inspect} instead of recompiling"
end
Nanoc::Int::NotificationCenter.on(:filtering_started) do |rep, filter_name|
puts "*** Started filtering #{rep.inspect} with #{filter_name}"
end
Nanoc::Int::NotificationCenter.on(:filtering_ended) do |rep, filter_name|
puts "*** Ended filtering #{rep.inspect} with #{filter_name}"
end
Nanoc::Int::NotificationCenter.on(:visit_started) do |item|
puts "*** Started visiting #{item.inspect}"
end
Nanoc::Int::NotificationCenter.on(:visit_ended) do |item|
puts "*** Ended visiting #{item.inspect}"
end
Nanoc::Int::NotificationCenter.on(:dependency_created) do |src, dst|
puts "*** Dependency created from #{src.inspect} onto #{dst.inspect}"
end
end
end
# Prints file actions (created, updated, deleted, identical, skipped)
class FileActionPrinter < Listener
def initialize(reps:)
@start_times = {}
@reps = reps
end
# @see Listener#start
def start
Nanoc::Int::NotificationCenter.on(:compilation_started) do |rep|
@start_times[rep.raw_path] = Time.now
end
Nanoc::Int::NotificationCenter.on(:rep_written) do |_rep, path, is_created, is_modified|
duration = path && @start_times[path] ? Time.now - @start_times[path] : nil
action =
case
when is_created then :create
when is_modified then :update
else :identical
end
level =
case
when is_created then :high
when is_modified then :high
else :low
end
log(level, action, path, duration)
end
end
# @see Listener#stop
def stop
super
@reps.select { |r| !r.compiled? }.each do |rep|
rep.raw_paths.each do |_snapshot_name, raw_path|
log(:low, :skip, raw_path, nil)
end
end
end
private
def log(level, action, path, duration)
Nanoc::CLI::Logger.instance.file(level, action, path, duration)
end
end
attr_accessor :listener_classes
def initialize(options, arguments, command)
super
@listener_classes = default_listener_classes
end
def run
time_before = Time.now
load_site
puts 'Compiling site…'
run_listeners_while do
site.compile
prune
end
time_after = Time.now
puts
puts "Site compiled in #{format('%.2f', time_after - time_before)}s."
end
protected
def prune
if site.config[:prune][:auto_prune]
Nanoc::Extra::Pruner.new(site, exclude: prune_config_exclude).run
end
end
def default_listener_classes
[
Nanoc::CLI::Commands::Compile::DiffGenerator,
Nanoc::CLI::Commands::Compile::DebugPrinter,
Nanoc::CLI::Commands::Compile::TimingRecorder,
Nanoc::CLI::Commands::Compile::GCController,
Nanoc::CLI::Commands::Compile::FileActionPrinter,
]
end
def setup_listeners
@listeners =
@listener_classes
.select { |klass| klass.enable_for?(self) }
.map { |klass| klass.new(reps: reps) }
@listeners.each(&:start)
end
def listeners
@listeners
end
def run_listeners_while
setup_listeners
yield
ensure
teardown_listeners
end
def teardown_listeners
@listeners.each(&:stop)
end
def reps
site.compiler.reps
end
def prune_config
site.config[:prune] || {}
end
def prune_config_exclude
prune_config[:exclude] || {}
end
end
end
runner Nanoc::CLI::Commands::Compile
nanoc-4.1.4/lib/nanoc/cli/commands/show-rules.rb0000644000004100000410000000302612665031555021530 0ustar www-datawww-datausage 'show-rules [thing]'
aliases :explain
summary 'describe the rules for each item'
description "
Prints the rules used for all items and layouts in the current site.
"
module Nanoc::CLI::Commands
class ShowRules < ::Nanoc::CLI::CommandRunner
def run
load_site
@c = Nanoc::CLI::ANSIStringColorizer
@reps = site.compiler.reps
action_provider = site.compiler.action_provider
unless action_provider.respond_to?(:rules_collection)
raise(
::Nanoc::Int::Errors::GenericTrivial,
'The show-rules command can only be used for sites with the Rule DSL action provider.',
)
end
@rules = action_provider.rules_collection
site.items.sort_by(&:identifier).each { |e| explain_item(e) }
site.layouts.sort_by(&:identifier).each { |e| explain_layout(e) }
end
def explain_item(item)
puts "#{@c.c('Item ' + item.identifier, :bold, :yellow)}:"
@reps[item].each do |rep|
rule = @rules.compilation_rule_for(rep)
puts " Rep #{rep.name}: #{rule ? rule.pattern : '(none)'}"
end
puts
end
def explain_layout(layout)
puts "#{@c.c('Layout ' + layout.identifier, :bold, :yellow)}:"
found = false
@rules.layout_filter_mapping.each do |pattern, _|
if pattern.match?(layout.identifier)
puts " #{pattern}"
found = true
break
end
end
unless found
puts ' (none)'
end
puts
end
end
end
runner Nanoc::CLI::Commands::ShowRules
nanoc-4.1.4/lib/nanoc/cli/commands/nanoc.rb0000644000004100000410000000167412665031555020525 0ustar www-datawww-datausage 'nanoc command [options] [arguments]'
summary 'Nanoc, a static site compiler written in Ruby'
opt :l, :color, 'enable color' do
$stdout.remove_stream_cleaner(Nanoc::CLI::StreamCleaners::ANSIColors)
$stderr.remove_stream_cleaner(Nanoc::CLI::StreamCleaners::ANSIColors)
end
opt :d, :debug, 'enable debugging' do
Nanoc::CLI.debug = true
end
opt :h, :help, 'show the help message and quit' do |_value, cmd|
puts cmd.help
exit 0
end
opt :C, :'no-color', 'disable color' do
$stdout.add_stream_cleaner(Nanoc::CLI::StreamCleaners::ANSIColors)
$stderr.add_stream_cleaner(Nanoc::CLI::StreamCleaners::ANSIColors)
end
opt :V, :verbose, 'make output more detailed' do
Nanoc::CLI::Logger.instance.level = :low
end
opt :v, :version, 'show version information and quit' do
puts Nanoc.version_information
exit 0
end
opt :w, :warn, 'enable warnings' do
$-w = true
end
run do |_opts, _args, cmd|
cmd.command_named('compile').run([])
end
nanoc-4.1.4/lib/nanoc/cli/commands/show-data.rb0000644000004100000410000000715112665031555021312 0ustar www-datawww-datausage 'show-data'
aliases :debug
summary 'show data in this site'
description <<-EOS
Show information about all items, item representations and layouts in the
current site, along with dependency information.
EOS
module Nanoc::CLI::Commands
class ShowData < ::Nanoc::CLI::CommandRunner
def run
load_site
# Get data
items = site.items
layouts = site.layouts
# Get dependency tracker
compiler = site.compiler
compiler.load_stores
dependency_store = compiler.dependency_store
# Print data
print_item_dependencies(items, dependency_store)
print_item_rep_paths(items)
print_item_rep_outdatedness(items, compiler)
print_layouts(layouts, compiler)
end
protected
def sorted_with_prev(objects)
prev = nil
objects.sort_by(&:identifier).each do |object|
yield(object, prev)
prev = object
end
end
def sorted_reps_with_prev(items)
prev = nil
items.sort_by(&:identifier).each do |item|
item.reps.sort_by { |r| r.name.to_s }.each do |rep|
yield(rep, prev)
prev = rep
end
end
end
def print_header(title)
header = '=' * 78
header[3..(title.length + 5)] = " #{title} "
puts
puts header
puts
end
def print_item_dependencies(items, dependency_store)
print_header('Item dependencies')
sorted_with_prev(items) do |item, prev|
puts if prev
puts "item #{item.identifier} depends on:"
predecessors = dependency_store.objects_causing_outdatedness_of(item).sort_by { |i| i ? i.identifier : '' }
predecessors.each do |pred|
type =
case pred
when Nanoc::Int::Layout
'layout'
when Nanoc::Int::ItemRep
'item rep'
when Nanoc::Int::Item
'item'
end
if pred
puts " [ #{format '%6s', type} ] #{pred.identifier}"
else
puts ' ( removed item )'
end
end
puts ' (nothing)' if predecessors.empty?
end
end
def print_item_rep_paths(items)
print_header('Item representation paths')
sorted_reps_with_prev(items) do |rep, prev|
puts if prev
puts "item #{rep.item.identifier}, rep #{rep.name}:"
if rep.raw_paths.empty?
puts ' (not written)'
end
length = rep.raw_paths.keys.map { |s| s.to_s.length }.max
rep.raw_paths.each do |snapshot_name, raw_path|
puts format(" [ %-#{length}s ] %s", snapshot_name, raw_path)
end
end
end
def print_item_rep_outdatedness(items, compiler)
print_header('Item representation outdatedness')
sorted_reps_with_prev(items) do |rep, prev|
puts if prev
puts "item #{rep.item.identifier}, rep #{rep.name}:"
outdatedness_reason = compiler.outdatedness_checker.outdatedness_reason_for(rep)
if outdatedness_reason
puts " is outdated: #{outdatedness_reason.message}"
else
puts ' is not outdated'
end
end
end
def print_layouts(layouts, compiler)
print_header('Layouts')
sorted_with_prev(layouts) do |layout, prev|
puts if prev
puts "layout #{layout.identifier}:"
outdatedness_reason = compiler.outdatedness_checker.outdatedness_reason_for(layout)
if outdatedness_reason
puts " is outdated: #{outdatedness_reason.message}"
else
puts ' is not outdated'
end
puts
end
end
end
end
runner Nanoc::CLI::Commands::ShowData
nanoc-4.1.4/lib/nanoc/cli/commands/deploy.rb0000644000004100000410000000575112665031555020723 0ustar www-datawww-datausage 'deploy [options]'
summary 'deploy the compiled site'
description "
Deploys the compiled site. The compiled site contents in the output directory will be uploaded to the destination, which is specified using the `--target` option.
"
option :t, :target, 'specify the location to deploy to (default: `default`)', argument: :required
flag :C, :'no-check', 'do not run the issue checks marked for deployment'
flag :L, :list, 'list available locations to deploy to'
flag :D, :'list-deployers', 'list available deployers'
option :n, :'dry-run', 'show what would be deployed'
module Nanoc::CLI::Commands
class Deploy < ::Nanoc::CLI::CommandRunner
def run
load_site
# FIXME: ugly to preprocess here
site.compiler.action_provider.preprocess(site)
# List deployers
if options[:'list-deployers']
deployers = Nanoc::Int::PluginRegistry.instance.find_all(Nanoc::Extra::Deployer)
deployer_names = deployers.keys.sort_by(&:to_s)
puts 'Available deployers:'
deployer_names.each do |name|
puts " #{name}"
end
return
end
# Get & list configs
deploy_configs = site.config.fetch(:deploy, {})
if options[:list]
if deploy_configs.empty?
puts 'No deployment configurations.'
else
puts 'Available deployment configurations:'
deploy_configs.keys.each do |name|
puts " #{name}"
end
end
return
end
# Can't proceed further without a deploy config
if deploy_configs.empty?
raise Nanoc::Int::Errors::GenericTrivial, 'The site has no deployment configurations.'
end
# Get target
target = options.fetch(:target, :default).to_sym
config = deploy_configs.fetch(target) do
raise Nanoc::Int::Errors::GenericTrivial, "The site has no deployment configuration for #{target}."
end
# Get deployer
names = Nanoc::Extra::Deployer.all.keys
name = config.fetch(:kind) do
$stderr.puts 'Warning: The specified deploy target does not have a kind attribute. Assuming rsync.'
'rsync'
end
deployer_class = Nanoc::Extra::Deployer.named(name)
if deployer_class.nil?
raise Nanoc::Int::Errors::GenericTrivial, "The specified deploy target has an unrecognised kind “#{name}” (expected one of #{names.join(', ')})."
end
# Check
unless options[:'no-check']
runner = Nanoc::Extra::Checking::Runner.new(site)
if runner.dsl_present?
puts 'Running issue checks…'
ok = runner.run_for_deploy
unless ok
puts 'Issues found, deploy aborted.'
return
end
puts 'No issues found. Deploying!'
end
end
# Run
deployer = deployer_class.new(
site.config[:output_dir],
config,
dry_run: options[:'dry-run'])
deployer.run
end
end
end
runner Nanoc::CLI::Commands::Deploy
nanoc-4.1.4/lib/nanoc/cli/commands/show-plugins.rb0000644000004100000410000000532612665031555022064 0ustar www-datawww-datasummary 'show all available plugins'
aliases :info
usage 'show-plugins [options]'
description <<-EOS
Show a list of available plugins, including filters and data sources.
If the current directory contains a Nanoc web site, the plugins defined in this site will be shown as well.
EOS
module Nanoc::CLI::Commands
class ShowPlugins < ::Nanoc::CLI::CommandRunner
def run
# Check arguments
if arguments.any?
raise Nanoc::Int::Errors::GenericTrivial, "usage: #{command.usage}"
end
# Get list of plugins (before and after)
plugins_before = Nanoc::Int::PluginRegistry.instance.all
site.code_snippets if site
plugins_after = Nanoc::Int::PluginRegistry.instance.all
# Divide list of plugins into builtin and custom
plugins_builtin = plugins_before
plugins_custom = plugins_after - plugins_before
# Find max identifiers length
plugin_with_longest_identifiers = plugins_after.reduce do |longest, current|
longest[:identifiers].join(', ').size > current[:identifiers].join(', ').size ? longest : current
end
max_identifiers_length = plugin_with_longest_identifiers[:identifiers].join(', ').size
PLUGIN_CLASS_ORDER.each do |superclass|
plugins_with_this_superclass = {
builtin: plugins_builtin.select { |p| p[:superclass] == superclass },
custom: plugins_custom.select { |p| p[:superclass] == superclass },
}
# Print kind
kind = name_for_plugin_class(superclass)
puts "#{kind}:"
puts
# Print plugins organised by subtype
[:builtin, :custom].each do |type|
# Find relevant plugins
relevant_plugins = plugins_with_this_superclass[type]
# Print type
puts " #{type}:"
if relevant_plugins.empty?
puts ' (none)'
next
end
# Print plugins
relevant_plugins.sort_by { |k| k[:identifiers].join(', ') }.each do |plugin|
# Display
puts format(
" %-#{max_identifiers_length}s (%s)",
plugin[:identifiers].join(', '),
plugin[:class].to_s.sub(/^::/, ''),
)
end
end
puts
end
end
private
PLUGIN_CLASS_ORDER = [
Nanoc::Filter,
Nanoc::DataSource,
Nanoc::Extra::Deployer,
].freeze unless defined? PLUGIN_CLASS_ORDER
PLUGIN_CLASSES = {
Nanoc::Filter => 'Filters',
Nanoc::DataSource => 'Data Sources',
Nanoc::Extra::Deployer => 'Deployers',
}.freeze unless defined? PLUGIN_CLASSES
def name_for_plugin_class(klass)
PLUGIN_CLASSES[klass]
end
end
end
runner Nanoc::CLI::Commands::ShowPlugins
nanoc-4.1.4/lib/nanoc/cli/commands/prune.rb0000644000004100000410000000322212665031555020547 0ustar www-datawww-datausage 'prune'
summary 'remove files not managed by Nanoc from the output directory'
description <<-EOS
Find all files in the output directory that do not correspond to an item
managed by Nanoc and remove them. Since this is a hazardous operation, an
additional `--yes` flag is needed as confirmation.
Also see the `auto_prune` configuration option in `nanoc.yaml` (`config.yaml`
for older Nanoc sites), which will automatically prune after compilation.
EOS
flag :y, :yes, 'confirm deletion'
flag :n, :'dry-run', 'print files to be deleted instead of actually deleting them'
module Nanoc::CLI::Commands
class Prune < ::Nanoc::CLI::CommandRunner
def run
load_site
# FIXME: ugly to preprocess here
site.compiler.action_provider.preprocess(site)
site.compiler.build_reps
if options.key?(:yes)
Nanoc::Extra::Pruner.new(site, exclude: prune_config_exclude).run
elsif options.key?(:'dry-run')
Nanoc::Extra::Pruner.new(site, exclude: prune_config_exclude, dry_run: true).run
else
$stderr.puts 'WARNING: Since the prune command is a destructive command, it requires an additional --yes flag in order to work.'
$stderr.puts
$stderr.puts 'Please ensure that the output directory does not contain any files (such as images or stylesheets) that are necessary but are not managed by Nanoc. If you want to get a list of all files that would be removed, pass --dry-run.'
exit 1
end
end
protected
def prune_config
site.config[:prune] || {}
end
def prune_config_exclude
prune_config[:exclude] || {}
end
end
end
runner Nanoc::CLI::Commands::Prune
nanoc-4.1.4/lib/nanoc/cli/error_handler.rb0000644000004100000410000002454012665031555020451 0ustar www-datawww-datamodule Nanoc::CLI
# Catches errors and prints nice diagnostic messages, then exits.
#
# @api private
class ErrorHandler
# @param [Nanoc::CLI::Command, nil] command The command that is
# currently being executed, or nil if there is none
def initialize(command: nil)
@command = command
end
# Enables error handling in the given block.
#
# @param [Nanoc::CLI::Command, nil] command The command that is
# currently being executed, or nil if there is none
#
# @return [void]
def self.handle_while(command: nil, &block)
if @disabled
yield
else
new(command: command).handle_while(&block)
end
end
# Disables error handling. This is used by the test cases to prevent error
# from being handled by the CLI while tests are running.
def self.disable
@disabled = true
end
# Re-enables error handling after it was disabled. This is used by the test
# cases to prevent error from being handled by the CLI while tests are
# running.
def self.enable
@disabled = false
end
# Enables error handling in the given block. This method should not be
# called directly; use {Nanoc::CLI::ErrorHandler.handle_while} instead.
#
# @return [void]
def handle_while(&_block)
# Set exit handler
%w( INT TERM ).each do |signal|
Signal.trap(signal) do
puts
exit!(0)
end
end
# Set stack trace dump handler
if !defined?(RUBY_ENGINE) || RUBY_ENGINE != 'jruby'
begin
Signal.trap('USR1') do
puts 'Caught USR1; dumping a stack trace'
puts caller.map { |i| " #{i}" }.join("\n")
end
rescue ArgumentError
end
end
# Run
yield
rescue Nanoc::Int::Errors::GenericTrivial => e
$stderr.puts "Error: #{e.message}"
exit(1)
rescue Interrupt
exit(1)
rescue StandardError, ScriptError => e
print_error(e)
exit(1)
end
# Prints the given error to stderr. Includes message, possible resolution
# (see {#resolution_for}), compilation stack, backtrace, etc.
#
# @param [Error] error The error that should be described
#
# @return [void]
def self.print_error(error)
new.print_error(error)
end
# Prints the given error to stderr. Includes message, possible resolution
# (see {#resolution_for}), compilation stack, backtrace, etc.
#
# @param [Error] error The error that should be described
#
# @return [void]
def print_error(error)
write_compact_error(error, $stderr)
File.open('crash.log', 'w') do |io|
cio = Nanoc::CLI.wrap_in_cleaning_stream(io)
cio.add_stream_cleaner(::Nanoc::CLI::StreamCleaners::ANSIColors)
write_verbose_error(error, cio)
end
end
# Writes a compact representation of the error, suitable for a terminal, on
# the given stream (probably stderr).
#
# @param [Error] error The error that should be described
#
# @param [IO] stream The stream to write the description too
#
# @return [void]
def write_compact_error(error, stream)
# Header
stream.puts
stream.puts 'Captain! We’ve been hit!'
# Sections
write_error_message(stream, error)
write_compilation_stack(stream, error)
write_stack_trace(stream, error)
# Issue link
write_issue_link(stream)
end
# Writes a verbose representation of the error on the given stream.
#
# @param [Error] error The error that should be described
#
# @param [IO] stream The stream to write the description too
#
# @return [void]
def write_verbose_error(error, stream)
# Header
stream.puts "Crashlog created at #{Time.now}"
# Sections
write_error_message(stream, error, verbose: true)
write_compilation_stack(stream, error, verbose: true)
write_stack_trace(stream, error, verbose: true)
write_version_information(stream, verbose: true)
write_system_information(stream, verbose: true)
write_installed_gems(stream, verbose: true)
write_gemfile_lock(stream, verbose: true)
write_load_paths(stream, verbose: true)
end
protected
# @return [Boolean] true if debug output is enabled, false if not
#
# @see Nanoc::CLI.debug?
def debug?
Nanoc::CLI.debug?
end
# @return [Nanoc::Int::Site] The site that is currently being processed
def site
@command && @command.site
end
# @return [Nanoc::Int::Compiler] The compiler for the current site
def compiler
site && site.compiler
end
# @return [Array] The current compilation stack
def stack
(compiler && compiler.stack) || []
end
# @return [Hash] A hash containing the gem names as keys and gem versions as value
def gems_and_versions
gems = {}
Gem::Specification.find_all.sort_by { |s| [s.name, s.version] }.each do |spec|
gems[spec.name] ||= []
gems[spec.name] << spec.version.to_s
end
gems
end
# A hash that contains the name of the gem for a given required file. If a
# `#require` fails, the gem name is looked up in this hash.
GEM_NAMES = {
'adsf' => 'adsf',
'bluecloth' => 'bluecloth',
'builder' => 'builder',
'coderay' => 'coderay',
'cri' => 'cri',
'erubis' => 'erubis',
'escape' => 'escape',
'fog' => 'fog',
'haml' => 'haml',
'handlebars' => 'hbs',
'json' => 'json',
'kramdown' => 'kramdown',
'less' => 'less',
'listen' => 'listen',
'markaby' => 'markaby',
'maruku' => 'maruku',
'mime/types' => 'mime-types',
'nokogiri' => 'nokogiri',
'pry' => 'pry',
'rack' => 'rack',
'rack/cache' => 'rack-cache',
'rainpress' => 'rainpress',
'rdiscount' => 'rdiscount',
'redcarpet' => 'redcarpet',
'redcloth' => 'RedCloth',
'rubypants' => 'rubypants',
'sass' => 'sass',
'w3c_validators' => 'w3c_validators',
}.freeze
# Attempts to find a resolution for the given error, or nil if no
# resolution can be automatically obtained.
#
# @param [Error] error The error to find a resolution for
#
# @return [String] The resolution for the given error
def resolution_for(error)
case error
when LoadError
# Get gem name
matches = error.message.match(/(no such file to load|cannot load such file) -- ([^\s]+)/)
return nil if matches.nil?
gem_name = GEM_NAMES[matches[2]]
# Build message
if gem_name
if using_bundler?
'Make sure the gem is added to Gemfile and run `bundle install`.'
else
"Install the '#{gem_name}' gem using `gem install #{gem_name}`."
end
end
when RuntimeError
if error.message =~ /^can't modify frozen/
'You attempted to modify immutable data. Some data, such as ' \
'item/layout attributes and raw item/layout content, can not ' \
'be modified once compilation has started. (This was ' \
'unintentionally possible in 3.1.x and before, but has been ' \
'disabled in 3.2.x in order to allow compiler optimisations.)'
end
end
end
def using_bundler?
defined?(Bundler) && Bundler::SharedHelpers.in_bundle?
end
def write_section_header(stream, title, verbose: false)
stream.puts
if verbose
stream.puts '===== ' + title.upcase + ':'
else
stream.puts "\e[1m\e[31m" + title + ':' + "\e[0m"
end
stream.puts
end
def write_error_message(stream, error, verbose: false)
write_section_header(stream, 'Message', verbose: verbose)
stream.puts "#{error.class}: #{error.message}"
resolution = resolution_for(error)
stream.puts resolution.to_s if resolution
end
def write_compilation_stack(stream, _error, verbose: false)
write_section_header(stream, 'Compilation stack', verbose: verbose)
if stack.empty?
stream.puts ' (empty)'
else
stack.reverse_each do |obj|
if obj.is_a?(Nanoc::Int::ItemRep)
stream.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
else # layout
stream.puts " - [layout] #{obj.identifier}"
end
end
end
end
def write_stack_trace(stream, error, verbose: false)
write_section_header(stream, 'Stack trace', verbose: verbose)
count = verbose ? -1 : 10
error.backtrace[0...count].each_with_index do |item, index|
stream.puts " #{index}. #{item}"
end
if !verbose && error.backtrace.size > count
stream.puts " ... #{error.backtrace.size - count} more lines omitted. See full crash log for details."
end
end
def write_issue_link(stream, _params = {})
stream.puts
stream.puts 'If you believe this is a bug in Nanoc, please do report it at'
stream.puts '-> https://github.com/nanoc/nanoc/issues/new <-'
stream.puts
stream.puts 'A detailed crash log has been written to ./crash.log.'
end
def write_version_information(stream, verbose: false)
write_section_header(stream, 'Version information', verbose: verbose)
stream.puts Nanoc.version_information
end
def write_system_information(stream, verbose: false)
uname = `uname -a`
write_section_header(stream, 'System information', verbose: verbose)
stream.puts uname
rescue Errno::ENOENT
end
def write_installed_gems(stream, verbose: false)
write_section_header(stream, 'Installed gems', verbose: verbose)
gems_and_versions.each do |g|
stream.puts " #{g.first} #{g.last.join(', ')}"
end
end
def write_gemfile_lock(stream, verbose: false)
if File.exist?('Gemfile.lock')
write_section_header(stream, 'Gemfile.lock', verbose: verbose)
stream.puts File.read('Gemfile.lock')
end
end
def write_load_paths(stream, verbose: false)
write_section_header(stream, 'Load paths', verbose: verbose)
$LOAD_PATH.each_with_index do |i, index|
stream.puts " #{index}. #{i}"
end
end
end
end
nanoc-4.1.4/lib/nanoc/cli/logger.rb0000644000004100000410000000356312665031555017104 0ustar www-datawww-datarequire 'singleton'
module Nanoc::CLI
# Nanoc::CLI::Logger is a singleton class responsible for generating
# feedback in the terminal.
#
# @api private
class Logger
# Maps actions (`:create`, `:update`, `:identical`, `:skip` and `:delete`)
# onto their ANSI color codes.
ACTION_COLORS = {
create: "\e[32m", # green
update: "\e[33m", # yellow
identical: '', # (nothing)
skip: '', # (nothing)
delete: "\e[31m" # red
}.freeze
include Singleton
# Returns the log level, which can be :high, :low or :off (which will log
# all messages, only high-priority messages, or no messages at all,
# respectively).
#
# @return [Symbol] The log level
attr_accessor :level
def initialize
@level = :high
end
# Logs a file-related action.
#
# @param [:high, :low] level The importance of this action
#
# @param [:create, :update, :identical, :skip, :delete] action The kind of file action
#
# @param [String] name The name of the file the action was performed on
#
# @return [void]
def file(level, action, name, duration = nil)
log(
level,
format(
'%s%12s%s %s%s',
ACTION_COLORS[action.to_sym],
action,
"\e[0m",
duration.nil? ? '' : format('[%2.2fs] ', duration),
name,
),
)
end
# Logs a message.
#
# @param [:high, :low] level The importance of this message
#
# @param [String] message The message to be logged
#
# @param [#puts] io The stream to which the message should be written
#
# @return [void]
def log(level, message, io = $stdout)
# Don't log when logging is disabled
return if @level == :off
# Log when level permits it
io.puts(message) if @level == :low || @level == level
end
end
end
nanoc-4.1.4/lib/nanoc/cli/stream_cleaners/0000755000004100000410000000000012665031555020440 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/cli/stream_cleaners/ansi_colors.rb0000644000004100000410000000036412665031555023303 0ustar www-datawww-datamodule Nanoc::CLI::StreamCleaners
# Removes ANSI color escape sequences.
#
# @api private
class ANSIColors < Abstract
# @see Nanoc::CLI::StreamCleaners::Abstract#clean
def clean(s)
s.gsub(/\e\[.+?m/, '')
end
end
end
nanoc-4.1.4/lib/nanoc/cli/stream_cleaners/utf8.rb0000644000004100000410000000061712665031555021657 0ustar www-datawww-datamodule Nanoc::CLI::StreamCleaners
# Simplifies output by replacing UTF-8 characters with their ASCII decompositions.
#
# @api private
class UTF8 < Abstract
# @see Nanoc::CLI::StreamCleaners::Abstract#clean
def clean(s)
# FIXME: this decomposition is not generally usable
s.gsub(/“|”/, '"').gsub(/‘|’/, '\'').gsub('…', '...').gsub('©', '(c)')
end
end
end
nanoc-4.1.4/lib/nanoc/cli/stream_cleaners/abstract.rb0000644000004100000410000000123312665031555022567 0ustar www-datawww-datamodule Nanoc::CLI::StreamCleaners
# Superclass for all stream cleaners. Stream cleaners have a single method,
# {#clean}, that takes a string and returns a cleaned string. Stream cleaners
# can have state, so they can act as a FSM.
#
# @abstract Subclasses must implement {#clean}
#
# @api private
class Abstract
# Returns a cleaned version of the given string.
#
# @param [String] s The string to clean
#
# @return [String] The cleaned string
def clean(s) # rubocop:disable Lint/UnusedMethodArgument
raise NotImplementedError, 'Subclasses of Nanoc::CLI::StreamCleaners::Abstract must implement #clean'
end
end
end
nanoc-4.1.4/lib/nanoc/cli/stream_cleaners.rb0000644000004100000410000000040412665031555020763 0ustar www-datawww-datamodule Nanoc::CLI
# @api private
module StreamCleaners
autoload 'Abstract', 'nanoc/cli/stream_cleaners/abstract'
autoload 'ANSIColors', 'nanoc/cli/stream_cleaners/ansi_colors'
autoload 'UTF8', 'nanoc/cli/stream_cleaners/utf8'
end
end
nanoc-4.1.4/lib/nanoc/cli/cleaning_stream.rb0000644000004100000410000000603212665031555020752 0ustar www-datawww-datamodule Nanoc::CLI
# An output stream that passes output through stream cleaners. This can be
# used to strip ANSI color sequences, for instance.
#
# @api private
class CleaningStream
# @param [IO, StringIO] stream The stream to wrap
def initialize(stream)
@stream = stream
@stream_cleaners = []
end
# Adds a stream cleaner for the given class to this cleaning stream. If the
# cleaning stream already has the given stream cleaner, nothing happens.
#
# @param [Nanoc::CLI::StreamCleaners::Abstract] klass The class of the
# stream cleaner to add
#
# @return [void]
def add_stream_cleaner(klass)
unless @stream_cleaners.map(&:class).include?(klass)
@stream_cleaners << klass.new
end
end
# Removes the stream cleaner for the given class from this cleaning stream.
# If the cleaning stream does not have the given stream cleaner, nothing
# happens.
#
# @param [Nanoc::CLI::StreamCleaners::Abstract] klass The class of the
# stream cleaner to add
#
# @return [void]
def remove_stream_cleaner(klass)
@stream_cleaners.delete_if { |c| c.class == klass }
end
# @group IO proxy methods
# @see IO#write
def write(s)
_nanoc_swallow_broken_pipe_errors_while do
@stream.write(_nanoc_clean(s))
end
end
# @see IO#<<
def <<(s)
_nanoc_swallow_broken_pipe_errors_while do
@stream.<<(_nanoc_clean(s))
end
end
# @see IO#tty?
def tty?
@cached_is_tty ||= @stream.tty?
end
# @see IO#flush
def flush
_nanoc_swallow_broken_pipe_errors_while do
@stream.flush
end
end
# @see IO#tell
def tell
@stream.tell
end
# @see IO#print
def print(s)
_nanoc_swallow_broken_pipe_errors_while do
@stream.print(_nanoc_clean(s))
end
end
# @see IO#puts
def puts(*s)
_nanoc_swallow_broken_pipe_errors_while do
@stream.puts(*s.map { |ss| _nanoc_clean(ss) })
end
end
# @see StringIO#string
def string
@stream.string
end
# @see IO#reopen
def reopen(*a)
@stream.reopen(*a)
end
# @see IO#close
def close
@stream.close
end
# @see File#exist?
def exist?
@stream.exist?
end
# @see File.exists?
def exists?
@stream.exists?
end
# @see IO.winsize
def winsize
@stream.winsize
end
# @see IO.winsize=
def winsize=(arg)
@stream.winsize = arg
end
# @see IO.sync
def sync
@stream.sync
end
# @see IO.sync=
def sync=(arg)
@stream.sync = arg
end
# @see IO.sync=
def external_encoding
@stream.external_encoding
end
# @see ARGF.set_encoding
def set_encoding(*args)
@stream.set_encoding(*args)
end
protected
def _nanoc_clean(s)
@stream_cleaners.reduce(s.to_s) { |a, e| e.clean(a) }
end
def _nanoc_swallow_broken_pipe_errors_while
yield
rescue Errno::EPIPE
end
end
end
nanoc-4.1.4/lib/nanoc/cli/command_runner.rb0000644000004100000410000000336712665031555020636 0ustar www-datawww-datamodule Nanoc::CLI
# A command runner subclass for Nanoc commands that adds Nanoc-specific
# convenience methods and error handling.
#
# @api private
class CommandRunner < ::Cri::CommandRunner
# @see http://rubydoc.info/gems/cri/Cri/CommandRunner#call-instance_method
#
# @return [void]
def call
Nanoc::CLI::ErrorHandler.handle_while(command: self) do
run
end
end
# Gets the site ({Nanoc::Int::Site} instance) in the current directory and
# loads its data.
#
# @return [Nanoc::Int::Site] The site in the current working directory
def site
# Load site if possible
@site ||= nil
if is_in_site_dir? && @site.nil?
@site = Nanoc::Int::SiteLoader.new.new_from_cwd
end
@site
end
# For debugging purposes.
#
# @api private
def site=(new_site)
@site = new_site
end
# @return [Boolean] true if the current working directory is a Nanoc site
# directory, false otherwise
def in_site_dir?
Nanoc::Int::SiteLoader.cwd_is_nanoc_site?
end
alias is_in_site_dir? in_site_dir?
# Asserts that the current working directory contains a site and loads the site into memory.
#
# @return [void]
def load_site
print 'Loading site… '
$stdout.flush
if site.nil?
raise ::Nanoc::Int::Errors::GenericTrivial, 'The current working directory does not seem to be a Nanoc site.'
end
puts 'done'
end
# @return [Boolean] true if debug output is enabled, false if not
#
# @see Nanoc::CLI.debug?
def debug?
Nanoc::CLI.debug?
end
protected
# @return [Array] The compilation stack.
def stack
(site && site.compiler.stack) || []
end
end
end
nanoc-4.1.4/lib/nanoc/base.rb0000644000004100000410000000215512665031555015764 0ustar www-datawww-datamodule Nanoc
autoload 'Error', 'nanoc/base/error'
autoload 'Filter', 'nanoc/base/compilation/filter'
end
# @api private
module Nanoc::Int
# Load helper classes
autoload 'Context', 'nanoc/base/context'
autoload 'Checksummer', 'nanoc/base/checksummer'
autoload 'DirectedGraph', 'nanoc/base/directed_graph'
autoload 'Errors', 'nanoc/base/errors'
autoload 'Memoization', 'nanoc/base/memoization'
autoload 'PluginRegistry', 'nanoc/base/plugin_registry'
# Load compilation classes
autoload 'Compiler', 'nanoc/base/compilation/compiler'
autoload 'DependencyTracker', 'nanoc/base/compilation/dependency_tracker'
autoload 'ItemRepRepo', 'nanoc/base/compilation/item_rep_repo'
autoload 'OutdatednessChecker', 'nanoc/base/compilation/outdatedness_checker'
autoload 'OutdatednessReasons', 'nanoc/base/compilation/outdatedness_reasons'
end
require_relative 'base/core_ext'
require_relative 'base/entities'
require_relative 'base/repos'
require_relative 'base/services'
require_relative 'base/views'
nanoc-4.1.4/lib/nanoc/helpers/0000755000004100000410000000000012665031555016164 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/helpers/breadcrumbs.rb0000644000004100000410000000250412665031555021003 0ustar www-datawww-datamodule Nanoc::Helpers
# Provides support for breadcrumbs, which allow the user to go up in the
# page hierarchy.
module Breadcrumbs
class CannotGetBreadcrumbsForNonLegacyItem < Nanoc::Int::Errors::Generic
def initialize(identifier)
super("You cannot build a breadcrumbs trail for an item that has a “full” identifier (#{identifier}). Doing so is only possible for items that have a legacy identifier.")
end
end
# Creates a breadcrumb trail leading from the current item to its parent,
# to its parent’s parent, etc, until the root item is reached. This
# function does not require that each intermediate item exist; for
# example, if there is no `/foo/` item, breadcrumbs for a `/foo/bar/` item
# will contain a `nil` element.
#
# @return [Array] The breadcrumbs, starting with the root item and ending
# with the item itself
def breadcrumbs_trail
unless @item.identifier.legacy?
raise CannotGetBreadcrumbsForNonLegacyItem.new(@item.identifier)
end
trail = []
idx_start = 0
loop do
idx = @item.identifier.to_s.index('/', idx_start)
break if idx.nil?
idx_start = idx + 1
identifier = @item.identifier.to_s[0..idx]
trail << @items[identifier]
end
trail
end
end
end
nanoc-4.1.4/lib/nanoc/helpers/tagging.rb0000644000004100000410000000440712665031555020136 0ustar www-datawww-datamodule Nanoc::Helpers
# Provides support for managing tags added to items.
#
# To add tags to items, set the `tags` attribute to an array of tags that
# should be applied to the item.
#
# @example Adding tags to an item
#
# tags: [ 'foo', 'bar', 'baz' ]
module Tagging
require 'nanoc/helpers/html_escape'
include Nanoc::Helpers::HTMLEscape
# Returns a formatted list of tags for the given item as a string. The
# tags will be linked using the {#link_for_tag} function; the
# HTML-escaping rules for {#link_for_tag} apply here as well.
#
# @param [String] base_url The URL to which the tag will be appended
# to construct the link URL. This URL must have a trailing slash. The
# function will return a tags string without tag page link if the param
# is not provided.
#
# @param [String] none_text The text to display when
# the item has no tags
#
# @param [String] separator The separator to put between tags
#
# @return [String] A hyperlinked list of tags for the given item
def tags_for(item, base_url: nil, none_text: '(none)', separator: ', ')
if item[:tags].nil? || item[:tags].empty?
none_text
else
item[:tags].map { |tag| base_url ? link_for_tag(tag, base_url) : tag }.join(separator)
end
end
# Find all items with the given tag.
#
# @param [String] tag The tag for which to find all items
#
# @return [Array] All items with the given tag
def items_with_tag(tag)
@items.select { |i| (i[:tags] || []).include?(tag) }
end
# Returns a link to to the specified tag. The link is marked up using the
# rel-tag microformat. The `href` attribute of the link will be HTML-
# escaped, as will the content of the `a` element.
#
# @param [String] tag The name of the tag, which should consist of letters
# and numbers (no spaces, slashes, or other special characters).
#
# @param [String] base_url The URL to which the tag will be appended to
# construct the link URL. This URL must have a trailing slash.
#
# @return [String] A link for the given tag and the given base URL
def link_for_tag(tag, base_url)
%(#{h tag})
end
end
end
nanoc-4.1.4/lib/nanoc/helpers/text.rb0000644000004100000410000000243112665031555017475 0ustar www-datawww-datamodule Nanoc::Helpers
# Contains several useful text-related helper functions.
module Text
# Returns an excerpt for the given string. HTML tags are ignored, so if
# you don't want them to turn up, they should be stripped from the string
# before passing it to the excerpt function.
#
# @param [String] string The string for which to build an excerpt
#
# @param [Number] length The maximum number of characters
# this excerpt can contain, including the omission.
#
# @param [String] omission The string to append to the
# excerpt when the excerpt is shorter than the original string
#
# @return [String] The excerpt of the given string
def excerptize(string, length: 25, omission: '...')
if string.length > length
excerpt_length = [0, length - omission.length].max
string[0...excerpt_length] + omission
else
string
end
end
# Strips all HTML tags out of the given string.
#
# @param [String] string The string from which to strip all HTML
#
# @return [String] The given string with all HTML stripped
def strip_html(string)
# FIXME: will need something more sophisticated than this, because it sucks
string.gsub(/<[^>]*(>+|\s*\z)/m, '').strip
end
end
end
nanoc-4.1.4/lib/nanoc/helpers/link_to.rb0000644000004100000410000001260112665031555020150 0ustar www-datawww-datamodule Nanoc::Helpers
# Contains functions for linking to items and item representations.
module LinkTo
require 'nanoc/helpers/html_escape'
include Nanoc::Helpers::HTMLEscape
# Creates a HTML link to the given path or item representation, and with
# the given text. All attributes of the `a` element, including the `href`
# attribute, will be HTML-escaped; the contents of the `a` element, which
# can contain markup, will not be HTML-escaped. The HTML-escaping is done
# using {Nanoc::Helpers::HTMLEscape#html_escape}.
#
# @param [String] text The visible link text
#
# @param [String, Nanoc::Int::Item, Nanoc::Int::ItemRep] target The path/URL,
# item or item representation that should be linked to
#
# @param [Hash] attributes A hash containing HTML attributes (e.g.
# `rel`, `title`, …) that will be added to the link.
#
# @return [String] The link text
#
# @example Linking to a path
#
# link_to('Blog', '/blog/')
# # => 'Blog'
#
# @example Linking to an item
#
# about = @items.find { |i| i.identifier == '/about/' }
# link_to('About Me', about)
# # => 'About Me'
#
# @example Linking to an item representation
#
# about = @items.find { |i| i.identifier == '/about/' }
# link_to('My vCard', about.rep(:vcard))
# # => 'My vCard'
#
# @example Linking with custom attributes
#
# link_to('Blog', '/blog/', :title => 'My super cool blog')
# # => 'Blog'
def link_to(text, target, attributes = {})
# Find path
path =
case target
when String
target
when Nanoc::ItemWithRepsView, Nanoc::ItemWithoutRepsView, Nanoc::ItemRepView
raise "Cannot create a link to #{target.inspect} because this target is not outputted (its routing rule returns nil)" if target.path.nil?
target.path
else
raise ArgumentError, "Cannot link to #{target.inspect} (expected a string or an item, not a #{target.class.name})"
end
# Join attributes
attributes = attributes.reduce('') do |memo, (key, value)|
memo + key.to_s + '="' + h(value) + '" '
end
# Create link
"#{text}"
end
# Creates a HTML link using {#link_to}, except when the linked item is
# the current one. In this case, a span element with class “active” and
# with the given text will be returned. The HTML-escaping rules for
# {#link_to} apply here as well.
#
# @param [String] text The visible link text
#
# @param [String, Nanoc::Int::Item, Nanoc::Int::ItemRep] target The path/URL,
# item or item representation that should be linked to
#
# @param [Hash] attributes A hash containing HTML attributes (e.g.
# `rel`, `title`, …) that will be added to the link.
#
# @return [String] The link text
#
# @example Linking to a different page
#
# link_to_unless_current('Blog', '/blog/')
# # => 'Blog'
#
# @example Linking to the same page
#
# link_to_unless_current('This Item', @item)
# # => 'This Item'
def link_to_unless_current(text, target, attributes = {})
# Find path
path = target.is_a?(String) ? target : target.path
if @item_rep && @item_rep.path == path
# Create message
"#{text}"
else
link_to(text, target, attributes)
end
end
# Returns the relative path from the current item to the given path or
# item representation. The returned path will not be HTML-escaped.
#
# @param [String, Nanoc::Int::Item, Nanoc::Int::ItemRep] target The path/URL,
# item or item representation to which the relative path should be
# generated
#
# @return [String] The relative path to the target
#
# @example
#
# # if the current item's path is /foo/bar/
# relative_path_to('/foo/qux/')
# # => '../qux/'
def relative_path_to(target)
require 'pathname'
# Find path
if target.is_a?(String)
path = target
else
path = target.path
if path.nil?
raise "Cannot get the relative path to #{target.inspect} because this target is not outputted (its routing rule returns nil)"
end
end
# Handle Windows network (UNC) paths
if path.start_with?('//', '\\\\')
return path
end
# Get source and destination paths
dst_path = Pathname.new(path)
if @item_rep.path.nil?
raise "Cannot get the relative path to #{path} because the current item representation, #{@item_rep.inspect}, is not outputted (its routing rule returns nil)"
end
src_path = Pathname.new(@item_rep.path)
# Calculate the relative path (method depends on whether destination is
# a directory or not).
relative_path =
if src_path.to_s[-1, 1] != '/'
dst_path.relative_path_from(src_path.dirname).to_s
else
dst_path.relative_path_from(src_path).to_s
end
# Add trailing slash if necessary
if dst_path.to_s[-1, 1] == '/'
relative_path << '/'
end
# Done
relative_path
end
end
end
nanoc-4.1.4/lib/nanoc/helpers/filtering.rb0000644000004100000410000000347012665031555020500 0ustar www-datawww-datamodule Nanoc::Helpers
# Provides functionality for filtering parts of an item or a layout.
module Filtering
require 'nanoc/helpers/capturing'
include Nanoc::Helpers::Capturing
# Filters the content in the given block and outputs it. This function
# does not return anything; instead, the filtered contents is directly
# appended to the output buffer (`_erbout`).
#
# This function has been tested with ERB and Haml. Other filters may not
# work correctly.
#
# @example Running a filter on a part of an item or layout
#
# Lorem ipsum dolor sit amet...
# <% filter :rubypants do %>
# Consectetur adipisicing elit...
# <% end %>
#
# @param [Symbol] filter_name The name of the filter to run on the
# contents of the block
#
# @param [Hash] arguments Arguments to pass to the filter
#
# @return [void]
def filter(filter_name, arguments = {}, &block)
# Capture block
data = capture(&block)
# Find filter
klass = Nanoc::Filter.named(filter_name)
raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if klass.nil?
# Create filter
assigns = {
item: @item,
rep: @rep,
item_rep: @item_rep,
items: @items,
layouts: @layouts,
config: @config,
site: @site,
content: @content,
}
filter = klass.new(assigns)
# Filter captured data
Nanoc::Int::NotificationCenter.post(:filtering_started, @item_rep.unwrap, filter_name)
filtered_data = filter.setup_and_run(data, arguments)
Nanoc::Int::NotificationCenter.post(:filtering_ended, @item_rep.unwrap, filter_name)
# Append filtered data to buffer
buffer = eval('_erbout', block.binding)
buffer << filtered_data
end
end
end
nanoc-4.1.4/lib/nanoc/helpers/html_escape.rb0000644000004100000410000000276012665031555021002 0ustar www-datawww-datamodule Nanoc::Helpers
# Contains functionality for HTML-escaping strings.
module HTMLEscape
require 'nanoc/helpers/capturing'
include Nanoc::Helpers::Capturing
# Returns the HTML-escaped representation of the given string or the given
# block. Only `&`, `<`, `>` and `"` are escaped. When given a block, the
# contents of the block will be escaped and appended to the output buffer,
# `_erbout`.
#
# @example Escaping a string
#
# h('
')
# # => '<br>'
#
# @example Escaping with a block
#
# <% h do %>
# Hello world!
# <% end %>
# # The buffer will now contain “<h1>Hello <em>world</em>!</h1>”
#
# @param [String] string The string to escape
#
# @return [String] The escaped string
def html_escape(string = nil, &block)
if block_given?
# Capture and escape block
data = capture(&block)
escaped_data = html_escape(data)
# Append filtered data to buffer
buffer = eval('_erbout', block.binding)
buffer << escaped_data
elsif string
string
.gsub('&', '&')
.gsub('<', '<')
.gsub('>', '>')
.gsub('"', '"')
else
raise 'The #html_escape or #h function needs either a ' \
'string or a block to HTML-escape, but neither a string nor a block was given'
end
end
alias h html_escape
end
end
nanoc-4.1.4/lib/nanoc/helpers/rendering.rb0000644000004100000410000001123712665031555020472 0ustar www-datawww-datamodule Nanoc::Helpers
# Provides functionality for rendering layouts as partials.
module Rendering
include Nanoc::Helpers::Capturing
# Renders the given layout. The given layout will be run through the first
# matching layout rule.
#
# When this method is invoked _without_ a block, the return value will be
# the rendered layout (a string) and `_erbout` will not be modified.
#
# When this method is invoked _with_ a block, an empty string will be
# returned and the rendered content will be appended to `_erbout`. In this
# case, the content of the block will be captured (using the
# {Nanoc::Helpers::Capturing} helper) and this content will be made
# available with `yield`. In other words, a `yield` inside the partial
# will output the content of the block passed to the method.
#
# (For the curious: the reason why {#render} with a block has this
# behaviour of returning an empty string and modifying `_erbout` is
# because ERB does not support combining the `<%= ... %>` form with a
# method call that takes a block.)
#
# The assigns (`@item`, `@config`, …) will be available in the partial. It
# is also possible to pass custom assigns to the method; these assigns
# will be made available as instance variables inside the partial.
#
# @param [String] identifier The identifier of the layout that should be
# rendered
#
# @param [Hash] other_assigns A hash containing extra assigns that will be
# made available as instance variables in the partial
#
# @example Rendering a head and a foot partial around some text
#
# <%= render 'head' %> - MIDDLE - <%= render 'foot' %>
# # => "HEAD - MIDDLE - FOOT"
#
# @example Rendering a head partial with a custom title
#
# # The 'head' layout
# <%= @title %>
#
# # The item/layout where the partial is rendered
# <%= render 'head', :title => 'Foo' %>
# # => "Foo
"
#
# @example Yielding inside a partial
#
# # The 'box' partial
#
# <%= yield %>
#
#
# # The item/layout where the partial is rendered
# <% render 'box' do %>
# I'm boxy! Luvz!
# <% end %>
#
# # Result
#
# I'm boxy! Luvz!
#
#
# @raise [Nanoc::Int::Errors::UnknownLayout] if the given layout does not
# exist
#
# @raise [Nanoc::Int::Errors::CannotDetermineFilter] if there is no layout
# rule for the given layout
#
# @raise [Nanoc::Int::Errors::UnknownFilter] if the layout rule for the given
# layout specifies an unknown filter
#
# @return [String, nil] The rendered partial, or nil if this method was
# invoked with a block
def render(identifier, other_assigns = {}, &block)
# Find layout
layout = @layouts[identifier]
layout ||= @layouts[identifier.__nanoc_cleaned_identifier]
raise Nanoc::Int::Errors::UnknownLayout.new(identifier) if layout.nil?
# Visit
Nanoc::Int::NotificationCenter.post(:visit_started, layout)
Nanoc::Int::NotificationCenter.post(:visit_ended, layout)
# Capture content, if any
captured_content = block_given? ? capture(&block) : nil
# Get assigns
assigns = {
content: captured_content,
item: @item,
item_rep: @item_rep,
items: @items,
layout: layout,
layouts: @layouts,
config: @config,
site: @site,
}.merge(other_assigns)
# Get filter name
filter_name, filter_args = *@site.unwrap.compiler.filter_name_and_args_for_layout(layout)
raise Nanoc::Int::Errors::CannotDetermineFilter.new(layout.identifier) if filter_name.nil?
# Get filter class
filter_class = Nanoc::Filter.named(filter_name)
raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if filter_class.nil?
# Create filter
filter = filter_class.new(assigns)
begin
# Notify start
Nanoc::Int::NotificationCenter.post(:processing_started, layout)
# Layout
content = layout.unwrap.content
arg = content.binary? ? content.filename : content.string
result = filter.setup_and_run(arg, filter_args)
# Append to erbout if we have a block
if block_given?
# Append result and return nothing
erbout = eval('_erbout', block.binding)
erbout << result
''
else
# Return result
result
end
ensure
# Notify end
Nanoc::Int::NotificationCenter.post(:processing_ended, layout)
end
end
end
end
nanoc-4.1.4/lib/nanoc/helpers/xml_sitemap.rb0000644000004100000410000000551112665031555021035 0ustar www-datawww-datamodule Nanoc::Helpers
# Contains functionality for building XML sitemaps that will be crawled by
# search engines. See the [Sitemaps protocol site](http://www.sitemaps.org)
# for details.
module XMLSitemap
# Builds an XML sitemap and returns it.
#
# The following attributes can optionally be set on items to change the
# behaviour of the sitemap:
#
# * `changefreq` — The estimated change frequency as defined by the
# Sitemaps protocol
#
# * `priority` — The item's priority, ranging from 0.0 to 1.0, as defined
# by the Sitemaps protocol
#
# The sitemap will also include dates on which the items were updated.
# These are generated automatically; the way this happens depends on the
# used data source (the filesystem data source checks the file mtimes, for
# instance).
#
# The site configuration will need to have the following attributes:
#
# * `base_url` — The URL to the site, without trailing slash. For example,
# if the site is at "http://example.com/", the `base_url` would be
# "http://example.com".
#
# @example Excluding binary items from the sitemap
#
# <%= xml_sitemap :items => @items.reject{ |i| i[:is_hidden] || i.binary? } %>
#
# @option params [Array] :items A list of items to include in the sitemap
#
# @option params [Proc] :rep_select A proc to filter reps through. If the
# proc returns true, the rep will be included; otherwise, it will not.
#
# @return [String] The XML sitemap
def xml_sitemap(params = {})
require 'builder'
# Extract parameters
items = params.fetch(:items) { @items.reject { |i| i[:is_hidden] } }
select_proc = params.fetch(:rep_select, nil)
# Create builder
buffer = ''
xml = Builder::XmlMarkup.new(target: buffer, indent: 2)
# Check for required attributes
if @config[:base_url].nil?
raise RuntimeError.new('The Nanoc::Helpers::XMLSitemap helper requires the site configuration to specify the base URL for the site.')
end
# Build sitemap
xml.instruct!
xml.urlset(xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9') do
# Add item
items.sort_by(&:identifier).each do |item|
reps = item.reps.reject { |r| r.raw_path.nil? }
reps.reject! { |r| !select_proc[r] } if select_proc
reps.sort_by { |r| r.name.to_s }.each do |rep|
xml.url do
xml.loc @config[:base_url] + rep.path
xml.lastmod item[:mtime].__nanoc_to_iso8601_date unless item[:mtime].nil?
xml.changefreq item[:changefreq] unless item[:changefreq].nil?
xml.priority item[:priority] unless item[:priority].nil?
end
end
end
end
# Return sitemap
buffer
end
end
end
nanoc-4.1.4/lib/nanoc/helpers/capturing.rb0000644000004100000410000001476212665031555020517 0ustar www-datawww-datamodule Nanoc::Helpers
# Provides functionality for “capturing” content in one place and reusing
# this content elsewhere.
#
# For example, suppose you want the sidebar of your site to contain a short
# summary of the item. You could put the summary in the meta file, but
# that’s not possible when the summary contains eRuby. You could also put
# the sidebar inside the actual item, but that’s not very pretty. Instead,
# you write the summary on the item itself, but capture it, and print it in
# the sidebar layout.
#
# This helper has been tested with ERB and Haml. Other filters may not work
# correctly.
#
# @example Capturing content for a summary
#
# <% content_for :summary do %>
# On this item, Nanoc is introduced, blah blah.
# <% end %>
#
# @example Showing captured content in a sidebar
#
#
module Capturing
# @api private
class CapturesStore
def initialize
@store = {}
end
def []=(item, name, content)
@store[item.identifier] ||= {}
@store[item.identifier][name] = content
end
def [](item, name)
@store[item.identifier] ||= {}
@store[item.identifier][name]
end
def reset_for(item)
@store[item.identifier] = {}
end
end
class ::Nanoc::Int::Site
# @api private
def captures_store
@captures_store ||= CapturesStore.new
end
# @api private
def captures_store_compiled_items
require 'set'
@captures_store_compiled_items ||= Set.new
end
end
# @overload content_for(name, params = {}, &block)
#
# Captures the content inside the block and stores it so that it can be
# referenced later on. The same method, {#content_for}, is used for
# getting the captured content as well as setting it. When capturing,
# the content of the block itself will not be outputted.
#
# By default, capturing content with the same name will raise an error if the newly captured
# content differs from the previously captured content. This behavior can be changed by
# providing a different `:existing` option to this method:
#
# * `:error`: When content already exists and is not identical, raise an error.
#
# * `:overwrite`: Overwrite the previously captured content with the newly captured content.
#
# * `:append`: Append the newly captured content to the previously captured content.
#
# @param [Symbol, String] name The base name of the attribute into which
# the content should be stored
#
# @option params [Symbol] existing Can be either `:error`, `:overwrite`, or `:append`
#
# @return [void]
#
# @overload content_for(item, name)
#
# Fetches the capture with the given name from the given item and
# returns it.
#
# @param [Nanoc::Int::Item] item The item for which to get the capture
#
# @param [Symbol, String] name The name of the capture to fetch
#
# @return [String] The stored captured content
def content_for(*args, &block)
if block_given? # Set content
# Get args
case args.size
when 1
name = args[0]
params = {}
when 2
name = args[0]
params = args[1]
else
raise ArgumentError, 'expected 1 or 2 argument (the name ' \
"of the capture, and optionally params) but got #{args.size} instead"
end
name = args[0]
existing_behavior = params.fetch(:existing, :error)
# Capture
content = capture(&block)
# Prepare for store
store = @site.unwrap.captures_store
case existing_behavior
when :overwrite
store[@item, name.to_sym] = ''
when :append
store[@item, name.to_sym] ||= ''
when :error
if store[@item, name.to_sym] && store[@item, name.to_sym] != content
raise "a capture named #{name.inspect} for #{@item.identifier} already exists"
else
store[@item, name.to_sym] = ''
end
else
raise ArgumentError, 'expected :existing_behavior param to #content_for to be one of ' \
":overwrite, :append, or :error, but #{existing_behavior.inspect} was given"
end
# Store
@site.unwrap.captures_store_compiled_items << @item.unwrap
store[@item, name.to_sym] << content
else # Get content
# Get args
if args.size != 2
raise ArgumentError, 'expected 2 arguments (the item ' \
"and the name of the capture) but got #{args.size} instead"
end
item = args[0].is_a?(Nanoc::ItemWithRepsView) ? args[0].unwrap : args[0]
name = args[1]
# Create dependency
if @item.nil? || item != @item.unwrap
Nanoc::Int::NotificationCenter.post(:visit_started, item)
Nanoc::Int::NotificationCenter.post(:visit_ended, item)
# This is an extremely ugly hack to get the compiler to recompile the
# item from which we use content. For this, we need to manually edit
# the content attribute to reset it. :(
# FIXME: clean this up
unless @site.unwrap.captures_store_compiled_items.include?(item)
@site.unwrap.captures_store.reset_for(item)
item.forced_outdated = true
@site.unwrap.compiler.reps[item].each do |r|
r.snapshot_contents = { last: item.content }
raise Nanoc::Int::Errors::UnmetDependency.new(r)
end
end
end
# Get content
@site.unwrap.captures_store[item, name.to_sym]
end
end
# Evaluates the given block and returns its contents. The contents of the
# block is not outputted.
#
# @return [String] The captured result
def capture(&block)
# Get erbout so far
erbout = eval('_erbout', block.binding)
erbout_length = erbout.length
# Execute block
yield
# Get new piece of erbout
erbout_addition = erbout[erbout_length..-1]
# Remove addition
erbout[erbout_length..-1] = ''
# Depending on how the filter outputs, the result might be a
# single string or an array of strings (slim outputs the latter).
erbout_addition = erbout_addition.join if erbout_addition.is_a? Array
# Done.
erbout_addition
end
end
end
nanoc-4.1.4/lib/nanoc/helpers/blogging.rb0000644000004100000410000003355412665031555020313 0ustar www-datawww-datamodule Nanoc::Helpers
# Provides functionality for building blogs, such as finding articles and
# constructing feeds.
#
# This helper has a few requirements. First, all blog articles should have
# the following attributes:
#
# * `kind` - Set to `"article"`
#
# * `created_at` - The article's publication timestamp
#
# Some functions in this blogging helper, such as the {#atom_feed} function,
# require additional attributes to be set; these attributes are described in
# the documentation for these functions.
#
# All "time" item attributes, site configuration attributes or method
# parameters can either be a `Time` instance or a string in any format
# parseable by `Time.parse`.
#
# The two main functions are {#sorted_articles} and {#atom_feed}.
module Blogging
# Returns an unsorted list of articles, i.e. items where the `kind`
# attribute is set to `"article"`.
#
# @return [Array] An array containing all articles
def articles
blk = -> { @items.select { |item| item[:kind] == 'article' } }
if @items.frozen?
@article_items ||= blk.call
else
blk.call
end
end
# Returns a sorted list of articles, i.e. items where the `kind`
# attribute is set to `"article"`. Articles are sorted by descending
# creation date, so newer articles appear before older articles.
#
# @return [Array] A sorted array containing all articles
def sorted_articles
blk = -> { articles.sort_by { |a| attribute_to_time(a[:created_at]) }.reverse }
if @items.frozen?
@sorted_article_items ||= blk.call
else
blk.call
end
end
class AtomFeedBuilder
include Nanoc::Helpers::Blogging
attr_accessor :config
attr_accessor :limit
attr_accessor :relevant_articles
attr_accessor :preserve_order
attr_accessor :content_proc
attr_accessor :excerpt_proc
attr_accessor :title
attr_accessor :author_name
attr_accessor :author_uri
attr_accessor :icon
attr_accessor :logo
def initialize(config, item)
@config = config
@item = item
end
def validate
validate_config
validate_feed_item
validate_articles
end
def build
buffer = ''
xml = Builder::XmlMarkup.new(target: buffer, indent: 2)
build_for_feed(xml)
buffer
end
protected
def sorted_relevant_articles
all = relevant_articles
unless @preserve_order
all = all.sort_by { |a| attribute_to_time(a[:created_at]) }
end
all.reverse.first(limit)
end
def last_article
sorted_relevant_articles.first
end
def validate_config
if @config[:base_url].nil?
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build Atom feed: site configuration has no base_url')
end
end
def validate_feed_item
if title.nil?
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build Atom feed: no title in params, item or site config')
end
if author_name.nil?
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build Atom feed: no author_name in params, item or site config')
end
if author_uri.nil?
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build Atom feed: no author_uri in params, item or site config')
end
end
def validate_articles
if relevant_articles.empty?
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build Atom feed: no articles')
end
if relevant_articles.any? { |a| a[:created_at].nil? }
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build Atom feed: one or more articles lack created_at')
end
end
def build_for_feed(xml)
xml.instruct!
xml.feed(xmlns: 'http://www.w3.org/2005/Atom') do
root_url = @config[:base_url] + '/'
# Add primary attributes
xml.id root_url
xml.title title
# Add date
xml.updated(attribute_to_time(last_article[:created_at]).__nanoc_to_iso8601_time)
# Add links
xml.link(rel: 'alternate', href: root_url)
xml.link(rel: 'self', href: feed_url)
# Add author information
xml.author do
xml.name author_name
xml.uri author_uri
end
# Add icon and logo
xml.icon icon if icon
xml.logo logo if logo
# Add articles
sorted_relevant_articles.each do |a|
build_for_article(a, xml)
end
end
end
def build_for_article(a, xml)
# Get URL
url = url_for(a)
return if url.nil?
xml.entry do
# Add primary attributes
xml.id atom_tag_for(a)
xml.title a[:title], type: 'html'
# Add dates
xml.published attribute_to_time(a[:created_at]).__nanoc_to_iso8601_time
xml.updated attribute_to_time(a[:updated_at] || a[:created_at]).__nanoc_to_iso8601_time
# Add specific author information
if a[:author_name] || a[:author_uri]
xml.author do
xml.name a[:author_name] || author_name
xml.uri a[:author_uri] || author_uri
end
end
# Add link
xml.link(rel: 'alternate', href: url)
# Add content
summary = excerpt_proc.call(a)
xml.content content_proc.call(a), type: 'html'
xml.summary summary, type: 'html' unless summary.nil?
end
end
end
# Returns a string representing the atom feed containing recent articles,
# sorted by descending creation date.
#
# The following attributes must be set on blog articles:
#
# * `title` - The title of the blog post
#
# * `created_at` (described above)
#
# * `kind` (described above) *unless* you are passing an explicit list of
# articles using the `:articles` parameter
#
# The following attributes can optionally be set on blog articles to
# change the behaviour of the Atom feed:
#
# * `excerpt` - An excerpt of the article, which is usually only a few
# lines long.
#
# * `custom_path_in_feed` - The path that will be used instead of the
# normal path in the feed. This can be useful when including
# non-outputted items in a feed; such items could have their custom feed
# path set to the blog path instead, for example.
#
# * `custom_url_in_feed` - The url that will be used instead of the
# normal url in the feed (generated from the site's base url + the item
# rep's path). This can be useful when building a link-blog where the
# URL of article is a remote location.
#
# * `updated_at` - The time when the article was last modified. If this
# attribute is not present, the `created_at` attribute will be used as
# the time when the article was last modified.
#
# The site configuration will need to have the following attributes:
#
# * `base_url` - The URL to the site, without trailing slash. For
# example, if the site is at "http://example.com/", the `base_url`
# would be "http://example.com".
#
# The feed item will need to know about the feed title, the feed author
# name, and the URI corresponding to the author. These can be specified
# using parameters, as attributes in the feed item, or in the site
# configuration.
#
# * `title` - The title of the feed, which is usually also the title of
# the blog.
#
# * `author_name` - The name of the item's author.
#
# * `author_uri` - The URI for the item's author, such as the author's
# web site URL.
#
# The feed item can have the following optional attributes:
#
# * `feed_url` - The custom URL of the feed. This can be useful when the
# private feed URL shouldn't be exposed; for example, when using
# FeedBurner this would be set to the public FeedBurner URL.
#
# To construct a feed, create a new item and make sure that it is
# filtered with `:erb` or `:erubis`; it should not be laid out. Ensure
# that it is routed to the proper path, e.g. `/blog.xml`. It may also be
# useful to set the `is_hidden` attribute to true, so that helpers such
# as the sitemap helper will ignore the item. The content of the feed
# item should be `<%= atom_feed %>`.
#
# @example Defining compilation and routing rules for a feed item
#
# compile '/blog/feed/' do
# filter :erb
# end
#
# route '/blog/feed/' do
# '/blog.xml'
# end
#
# @example Limiting the number of items in a feed
#
# <%= atom_feed :limit => 5 %>
#
# @option params [Number] :limit (5) The maximum number of articles to
# show
#
# @option params [Array] :articles (articles) A list of articles to include
# in the feed
#
# @option params [Boolean] :preserve_order (false) Whether or not the
# ordering of the list of articles should be preserved. If false, the
# articles will be sorted by `created_at`. If true, the list of articles
# will be used as-is, and should have the most recent articles last.
#
# @option params [Proc] :content_proc (->{ |article|
# article.compiled_content(:snapshot => :pre) }) A proc that returns the
# content of the given article, which is passed as a parameter. This
# function may not return nil.
#
# @option params [proc] :excerpt_proc (->{ |article| article[:excerpt] })
# A proc that returns the excerpt of the given article, passed as a
# parameter. This function should return nil if there is no excerpt.
#
# @option params [String] :title The feed's title, if it is not given in
# the item attributes.
#
# @option params [String] :author_name The name of the feed's author, if
# it is not given in the item attributes.
#
# @option params [String] :author_uri The URI of the feed's author, if it
# is not given in the item attributes.
#
# @option params [String] :icon The URI of the feed's icon.
#
# @option params [String] :logo The URI of the feed's logo.
#
# @return [String] The generated feed content
def atom_feed(params = {})
require 'builder'
# Create builder
builder = AtomFeedBuilder.new(@config, @item)
# Fill builder
builder.limit = params[:limit] || 5
builder.relevant_articles = params[:articles] || articles || []
builder.preserve_order = params.fetch(:preserve_order, false)
builder.content_proc = params[:content_proc] || ->(a) { a.compiled_content(snapshot: :pre) }
builder.excerpt_proc = params[:excerpt_proc] || ->(a) { a[:excerpt] }
builder.title = params[:title] || @item[:title] || @config[:title]
builder.author_name = params[:author_name] || @item[:author_name] || @config[:author_name]
builder.author_uri = params[:author_uri] || @item[:author_uri] || @config[:author_uri]
builder.icon = params[:icon]
builder.logo = params[:logo]
# Run
builder.validate
builder.build
end
# Returns the URL for the given item. It will return the URL containing
# the custom path in the feed if possible, otherwise the normal path.
#
# @param [Nanoc::Int::Item] item The item for which to fetch the URL.
#
# @return [String] The URL of the given item
def url_for(item)
# Check attributes
if @config[:base_url].nil?
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build Atom feed: site configuration has no base_url')
end
# Build URL
if item[:custom_url_in_feed]
item[:custom_url_in_feed]
elsif item[:custom_path_in_feed]
@config[:base_url] + item[:custom_path_in_feed]
elsif item.path
@config[:base_url] + item.path
end
end
# Returns the URL of the feed. It will return the custom feed URL if set,
# or otherwise the normal feed URL.
#
# @return [String] The URL of the feed
def feed_url
# Check attributes
if @config[:base_url].nil?
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build Atom feed: site configuration has no base_url')
end
@item[:feed_url] || @config[:base_url] + @item.path
end
# Returns an URI containing an unique ID for the given item. This will be
# used in the Atom feed to uniquely identify articles. These IDs are
# created using a procedure suggested by Mark Pilgrim and described in his
# ["How to make a good ID in Atom" blog post]
# (http://web.archive.org/web/20110915110202/http://diveintomark.org/archives/2004/05/28/howto-atom-id).
#
# @param [Nanoc::Int::Item] item The item for which to create an atom tag
#
# @return [String] The atom tag for the given item
def atom_tag_for(item)
hostname, base_dir = %r{^.+?://([^/]+)(.*)$}.match(@config[:base_url])[1..2]
formatted_date = attribute_to_time(item[:created_at]).__nanoc_to_iso8601_date
'tag:' + hostname + ',' + formatted_date + ':' + base_dir + (item.path || item.identifier.to_s)
end
# Converts the given attribute (which can be a string, a Time, a Date or a
# DateTime) into a Time. When given a Date instance or a string, the
# argument is assumed to be in the local timezone.
#
# @param [String, Time, Date, DateTime] arg Something that contains time
# information but is not necessarily a Time instance yet
#
# @return [Time] The Time instance corresponding to the given input
def attribute_to_time(arg)
case arg
when DateTime
arg.to_time
when Date
Time.local(arg.year, arg.month, arg.day)
when String
Time.parse(arg)
else
arg
end
end
end
end
nanoc-4.1.4/lib/nanoc/rule_dsl.rb0000644000004100000410000000052712665031555016664 0ustar www-datawww-datarequire_relative 'rule_dsl/compiler_dsl'
require_relative 'rule_dsl/action_provider'
require_relative 'rule_dsl/recording_executor'
require_relative 'rule_dsl/rule_context'
require_relative 'rule_dsl/rule_memory_calculator'
require_relative 'rule_dsl/rule'
require_relative 'rule_dsl/rules_collection'
require_relative 'rule_dsl/rules_loader'
nanoc-4.1.4/lib/nanoc/extra.rb0000644000004100000410000000076512665031555016202 0ustar www-datawww-data# @api private
module Nanoc::Extra
autoload 'Checking', 'nanoc/extra/checking'
autoload 'FilesystemTools', 'nanoc/extra/filesystem_tools'
autoload 'LinkCollector', 'nanoc/extra/link_collector.rb'
autoload 'Pruner', 'nanoc/extra/pruner'
autoload 'Piper', 'nanoc/extra/piper'
autoload 'JRubyNokogiriWarner', 'nanoc/extra/jruby_nokogiri_warner'
end
require 'nanoc/extra/core_ext'
require 'nanoc/extra/deployer'
require 'nanoc/extra/deployers'
nanoc-4.1.4/lib/nanoc/data_sources/0000755000004100000410000000000012665031555017176 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/data_sources/filesystem_unified.rb0000644000004100000410000000753412665031555023423 0ustar www-datawww-datamodule Nanoc::DataSources
# The filesystem_unified data source stores its items and layouts in nested
# directories. Items and layouts are represented by one or two files; if it
# is represented using one file, the metadata can be contained in this file.
#
# The default root directory for items is the `content` directory; for
# layouts, this is the `layouts` directory. This can be overridden
# in the data source configuration:
#
# data_sources:
# - type: filesystem_unified
# content_dir: items
# layouts_dir: layouts
#
# The metadata for items and layouts can be stored in a separate file with
# the same base name but with the `.yaml` extension. If such a file is
# found, metadata is read from that file. Alternatively, the content file
# itself can start with a metadata section: it can be stored at the top of
# the file, between `---` (three dashes) separators. For example:
#
# ---
# title: "Moo!"
# ---
# h1. Hello!
#
# The metadata section can be omitted. If the file does not start with
# three or five dashes, the entire file will be considered as content.
#
# The identifier of items and layouts is determined as follows. A file with
# an `index.*` filename, such as `index.txt`, will have the filesystem path
# with the `index.*` part stripped as a identifier. For example:
#
# foo/bar/index.html → /foo/bar/
#
# In other cases, the identifier is calculated by stripping the extension.
# If the `allow_periods_in_identifiers` attribute in the configuration is
# true, only the last extension will be stripped if the file has multiple
# extensions; if it is false or unset, all extensions will be stripped.
# For example:
#
# (`allow_periods_in_identifiers` set to true)
# foo.entry.html → /foo.entry/
#
# (`allow_periods_in_identifiers` set to false)
# foo.html.erb → /foo/
#
# Note that each item must have an unique identifier. Nanoc will display an
# error if two items with the same identifier are found.
#
# Some more examples:
#
# content/index.html → /
# content/foo.html → /foo/
# content/foo/index.html → /foo/
# content/foo/bar.html → /foo/bar/
# content/foo/bar.baz.html → /foo/bar/ OR /foo/bar.baz/
# content/foo/bar/index.html → /foo/bar/
# content/foo.bar/index.html → /foo.bar/
#
# The file extension does not determine the filters to run on items; the
# Rules file is used to specify processing instructors for each item.
#
# It is possible to set an explicit encoding that should be used when reading
# files. In the data source configuration, set `encoding` to an encoding
# understood by Ruby’s `Encoding`. If no encoding is set in the configuration,
# one will be inferred from the environment.
#
# @api private
class FilesystemUnified < Nanoc::DataSource
include Nanoc::DataSources::Filesystem
private
# See {Nanoc::DataSources::Filesystem#filename_for}.
def filename_for(base_filename, ext)
if ext.nil?
nil
elsif ext.empty?
base_filename
else
base_filename + '.' + ext
end
end
# Returns the identifier derived from the given filename, first stripping
# the given directory name off the filename.
def identifier_for_filename(filename)
if config[:identifier_type] == 'full'
return Nanoc::Identifier.new(filename)
end
regex =
if filename =~ /(^|\/)index(\.[^\/]+)?$/
@config && @config[:allow_periods_in_identifiers] ? /\/?(index)?(\.[^\/\.]+)?$/ : /\/?index(\.[^\/]+)?$/
else
@config && @config[:allow_periods_in_identifiers] ? /\.[^\/\.]+$/ : /\.[^\/]+$/
end
Nanoc::Identifier.new(filename.sub(regex, ''), type: :legacy)
end
end
end
nanoc-4.1.4/lib/nanoc/data_sources/filesystem.rb0000644000004100000410000002453612665031555021721 0ustar www-datawww-datamodule Nanoc::DataSources
# Provides functionality common across all filesystem data sources.
#
# @api private
module Filesystem
# See {Nanoc::DataSource#up}.
def up
end
# See {Nanoc::DataSource#down}.
def down
end
def content_dir_name
config.fetch(:content_dir, 'content')
end
def layouts_dir_name
config.fetch(:layouts_dir, 'layouts')
end
# See {Nanoc::DataSource#items}.
def items
load_objects(content_dir_name, 'item', Nanoc::Int::Item)
end
# See {Nanoc::DataSource#layouts}.
def layouts
load_objects(layouts_dir_name, 'layout', Nanoc::Int::Layout)
end
protected
# Creates instances of klass corresponding to the files in dir_name. The
# kind attribute indicates the kind of object that is being loaded and is
# used solely for debugging purposes.
#
# This particular implementation loads objects from a filesystem-based
# data source where content and attributes can be spread over two separate
# files. The content and meta-file are optional (but at least one of them
# needs to be present, obviously) and the content file can start with a
# metadata section.
#
# @see Nanoc::DataSources::Filesystem#load_objects
def load_objects(dir_name, kind, klass)
res = []
return [] if dir_name.nil?
all_split_files_in(dir_name).each do |base_filename, (meta_ext, content_exts)|
content_exts.each do |content_ext|
# Get filenames
meta_filename = filename_for(base_filename, meta_ext)
content_filename = filename_for(base_filename, content_ext)
# Read content and metadata
is_binary = content_filename && !@site_config[:text_extensions].include?(File.extname(content_filename)[1..-1])
if is_binary && klass == Nanoc::Int::Item
meta = (meta_filename && YAML.load_file(meta_filename)) || {}
content_or_filename = content_filename
elsif is_binary && klass == Nanoc::Int::Layout
raise "The layout file '#{content_filename}' is a binary file, but layouts can only be textual"
else
meta, content_or_filename = parse(content_filename, meta_filename, kind)
end
# Get attributes
attributes = {
filename: content_filename,
content_filename: content_filename,
meta_filename: meta_filename,
extension: content_filename ? ext_of(content_filename)[1..-1] : nil,
}.merge(meta)
# Get identifier
if content_filename
identifier = identifier_for_filename(content_filename[dir_name.length..-1])
elsif meta_filename
identifier = identifier_for_filename(meta_filename[dir_name.length..-1])
else
raise 'meta_filename and content_filename are both nil'
end
# Get modification times
meta_mtime = meta_filename ? File.stat(meta_filename).mtime : nil
content_mtime = content_filename ? File.stat(content_filename).mtime : nil
if meta_mtime && content_mtime
mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
elsif meta_mtime
mtime = meta_mtime
elsif content_mtime
mtime = content_mtime
else
raise 'meta_mtime and content_mtime are both nil'
end
attributes[:mtime] = mtime
# Create content
full_content_filename = content_filename && File.expand_path(content_filename)
content =
if is_binary
Nanoc::Int::BinaryContent.new(full_content_filename)
else
Nanoc::Int::TextualContent.new(content_or_filename, filename: full_content_filename)
end
# Create object
res << klass.new(content, attributes, identifier)
end
end
res
end
# e.g.
#
# {
# 'content/foo' => [ 'yaml', ['html', 'md'] ],
# 'content/bar' => [ 'yaml', [nil] ],
# 'content/qux' => [ nil, ['html'] ]
# }
def all_split_files_in(dir_name)
by_basename =
all_files_in(dir_name)
.reject { |fn| fn =~ /(~|\.orig|\.rej|\.bak)$/ }
.group_by { |fn| basename_of(fn) }
all = {}
by_basename.each_pair do |basename, filenames|
# Divide
meta_filenames = filenames.select { |fn| ext_of(fn) == '.yaml' }
content_filenames = filenames.select { |fn| ext_of(fn) != '.yaml' }
# Check number of files per type
unless [0, 1].include?(meta_filenames.size)
raise "Found #{meta_filenames.size} meta files for #{basename}; expected 0 or 1"
end
unless config[:identifier_type] == 'full'
unless [0, 1].include?(content_filenames.size)
raise "Found #{content_filenames.size} content files for #{basename}; expected 0 or 1"
end
end
all[basename] = []
all[basename][0] =
meta_filenames[0] ? 'yaml' : nil
all[basename][1] =
content_filenames.any? ? content_filenames.map { |fn| ext_of(fn)[1..-1] || '' } : [nil]
end
all
end
# Returns all files in the given directory and directories below it.
def all_files_in(dir_name)
Nanoc::Extra::FilesystemTools.all_files_in(dir_name, config[:extra_files])
end
# Returns the filename for the given base filename and the extension.
#
# If the extension is nil, this function should return nil as well.
#
# A simple implementation would simply concatenate the base filename, a
# period and an extension (which is what the
# {Nanoc::DataSources::FilesystemCompact} data source does), but other
# data sources may prefer to implement this differently (for example,
# {Nanoc::DataSources::FilesystemVerbose} doubles the last part of the
# basename before concatenating it with a period and the extension).
def filename_for(_base_filename, _ext)
raise NotImplementedError.new(
"#{self.class} does not implement #filename_for",
)
end
# Returns the identifier that corresponds with the given filename, which
# can be the content filename or the meta filename.
def identifier_for_filename(_filename)
raise NotImplementedError.new(
"#{self.class} does not implement #identifier_for_filename",
)
end
# Returns the base name of filename, i.e. filename with the first or all
# extensions stripped off. By default, all extensions are stripped off,
# but when allow_periods_in_identifiers is set to true in the site
# configuration, only the last extension will be stripped .
def basename_of(filename)
filename.sub(extension_regex, '')
end
# Returns the extension(s) of filename. Supports multiple extensions.
# Includes the leading period.
def ext_of(filename)
filename =~ extension_regex ? Regexp.last_match[1] : ''
end
# Returns a regex that is used for determining the extension of a file
# name. The first match group will be the entire extension, including the
# leading period.
def extension_regex
if @config && @config[:allow_periods_in_identifiers]
/(\.[^\/\.]+$)/
else
/(\.[^\/]+$)/
end
end
# Parses the file named `filename` and returns an array with its first
# element a hash with the file's metadata, and with its second element the
# file content itself.
def parse(content_filename, meta_filename, _kind)
# Read content and metadata from separate files
if meta_filename
content = content_filename ? read(content_filename) : ''
meta_raw = read(meta_filename)
begin
meta = YAML.load(meta_raw) || {}
rescue Exception => e
raise "Could not parse YAML for #{meta_filename}: #{e.message}"
end
verify_meta(meta, meta_filename)
return [meta, content]
end
# Read data
data = read(content_filename)
# Check presence of metadata section
if data !~ /\A-{3,5}\s*$/
return [{}, data]
end
# Split data
pieces = data.split(/^(-{5}|-{3})[ \t]*\r?\n?/, 3)
if pieces.size < 4
raise RuntimeError.new(
"The file '#{content_filename}' appears to start with a metadata section (three or five dashes at the top) but it does not seem to be in the correct format.",
)
end
# Parse
begin
meta = YAML.load(pieces[2]) || {}
rescue Exception => e
raise "Could not parse YAML for #{content_filename}: #{e.message}"
end
verify_meta(meta, content_filename)
content = pieces[4]
# Done
[meta, content]
end
class InvalidMetadataError < Nanoc::Error
def initialize(filename, klass)
super("The file #{filename} has invalid metadata (expected key-value pairs, found #{klass} instead)")
end
end
def verify_meta(meta, filename)
return if meta.is_a?(Hash)
raise InvalidMetadataError.new(filename, meta.class)
end
# Reads the content of the file with the given name and returns a string
# in UTF-8 encoding. The original encoding of the string is derived from
# the default external encoding, but this can be overridden by the
# “encoding” configuration attribute in the data source configuration.
def read(filename)
# Read
begin
data = File.read(filename)
rescue => e
raise RuntimeError.new("Could not read #{filename}: #{e.inspect}")
end
# Fix
if data.respond_to?(:encode!)
if @config && @config[:encoding]
original_encoding = Encoding.find(@config[:encoding])
data.force_encoding(@config[:encoding])
else
original_encoding = data.encoding
end
begin
data.encode!('UTF-8')
rescue
raise_encoding_error(filename, original_encoding)
end
unless data.valid_encoding?
raise_encoding_error(filename, original_encoding)
end
end
# Remove UTF-8 BOM (ugly)
data.delete!("\xEF\xBB\xBF")
data
end
# Raises an invalid encoding error for the given filename and encoding.
def raise_encoding_error(filename, encoding)
raise RuntimeError.new("Could not read #{filename} because the file is not valid #{encoding}.")
end
end
end
nanoc-4.1.4/lib/nanoc/data_sources.rb0000644000004100000410000000055212665031555017525 0ustar www-datawww-data# @api private
module Nanoc::DataSources
autoload 'Filesystem', 'nanoc/data_sources/filesystem'
autoload 'FilesystemUnified', 'nanoc/data_sources/filesystem_unified'
Nanoc::DataSource.register '::Nanoc::DataSources::FilesystemUnified', :filesystem_unified
Nanoc::DataSource.register '::Nanoc::DataSources::FilesystemUnified', :filesystem
end
nanoc-4.1.4/metadata.yml0000644000004100000410000003101712665031555015163 0ustar www-datawww-data--- !ruby/object:Gem::Specification
name: nanoc
version: !ruby/object:Gem::Version
version: 4.1.4
platform: ruby
authors:
- Denis Defreyne
autorequire:
bindir: bin
cert_chain: []
date: 2016-02-13 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: cri
requirement: !ruby/object:Gem::Requirement
requirements:
- - "~>"
- !ruby/object:Gem::Version
version: '2.3'
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - "~>"
- !ruby/object:Gem::Version
version: '2.3'
- !ruby/object:Gem::Dependency
name: bundler
requirement: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 1.7.10
- - "<"
- !ruby/object:Gem::Version
version: '2.0'
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 1.7.10
- - "<"
- !ruby/object:Gem::Version
version: '2.0'
description: Nanoc is a static-site generator focused on flexibility. It transforms
content from a format such as Markdown or AsciiDoc into another format, usually
HTML, and lays out pages consistently to retain the site’s look and feel throughout.
Static sites built with Nanoc can be deployed to any web server.
email: denis.defreyne@stoneship.org
executables:
- nanoc
extensions: []
extra_rdoc_files:
- ChangeLog
- LICENSE
- README.md
- NEWS.md
files:
- CONTRIBUTING.md
- ChangeLog
- Gemfile
- Gemfile.lock
- Guardfile
- LICENSE
- NEWS.md
- README.md
- Rakefile
- bin/nanoc
- doc/yardoc_handlers/identifier.rb
- doc/yardoc_templates/default/layout/html/footer.erb
- lib/nanoc.rb
- lib/nanoc/base.rb
- lib/nanoc/base/checksummer.rb
- lib/nanoc/base/compilation/compiler.rb
- lib/nanoc/base/compilation/dependency_tracker.rb
- lib/nanoc/base/compilation/filter.rb
- lib/nanoc/base/compilation/item_rep_repo.rb
- lib/nanoc/base/compilation/outdatedness_checker.rb
- lib/nanoc/base/compilation/outdatedness_reasons.rb
- lib/nanoc/base/context.rb
- lib/nanoc/base/core_ext.rb
- lib/nanoc/base/core_ext/array.rb
- lib/nanoc/base/core_ext/hash.rb
- lib/nanoc/base/core_ext/pathname.rb
- lib/nanoc/base/core_ext/string.rb
- lib/nanoc/base/directed_graph.rb
- lib/nanoc/base/entities.rb
- lib/nanoc/base/entities/code_snippet.rb
- lib/nanoc/base/entities/configuration.rb
- lib/nanoc/base/entities/content.rb
- lib/nanoc/base/entities/document.rb
- lib/nanoc/base/entities/identifiable_collection.rb
- lib/nanoc/base/entities/identifier.rb
- lib/nanoc/base/entities/item.rb
- lib/nanoc/base/entities/item_rep.rb
- lib/nanoc/base/entities/layout.rb
- lib/nanoc/base/entities/pattern.rb
- lib/nanoc/base/entities/rule_memory.rb
- lib/nanoc/base/entities/rule_memory_action.rb
- lib/nanoc/base/entities/rule_memory_actions.rb
- lib/nanoc/base/entities/rule_memory_actions/filter.rb
- lib/nanoc/base/entities/rule_memory_actions/layout.rb
- lib/nanoc/base/entities/rule_memory_actions/snapshot.rb
- lib/nanoc/base/entities/site.rb
- lib/nanoc/base/entities/snapshot_def.rb
- lib/nanoc/base/error.rb
- lib/nanoc/base/errors.rb
- lib/nanoc/base/memoization.rb
- lib/nanoc/base/plugin_registry.rb
- lib/nanoc/base/repos.rb
- lib/nanoc/base/repos/checksum_store.rb
- lib/nanoc/base/repos/compiled_content_cache.rb
- lib/nanoc/base/repos/config_loader.rb
- lib/nanoc/base/repos/data_source.rb
- lib/nanoc/base/repos/dependency_store.rb
- lib/nanoc/base/repos/rule_memory_store.rb
- lib/nanoc/base/repos/site_loader.rb
- lib/nanoc/base/repos/store.rb
- lib/nanoc/base/services.rb
- lib/nanoc/base/services/action_provider.rb
- lib/nanoc/base/services/compiler_loader.rb
- lib/nanoc/base/services/executor.rb
- lib/nanoc/base/services/item_rep_builder.rb
- lib/nanoc/base/services/item_rep_router.rb
- lib/nanoc/base/services/item_rep_selector.rb
- lib/nanoc/base/services/item_rep_writer.rb
- lib/nanoc/base/services/notification_center.rb
- lib/nanoc/base/services/temp_filename_factory.rb
- lib/nanoc/base/views.rb
- lib/nanoc/base/views/config_view.rb
- lib/nanoc/base/views/identifiable_collection_view.rb
- lib/nanoc/base/views/item_collection_with_reps_view.rb
- lib/nanoc/base/views/item_collection_without_reps_view.rb
- lib/nanoc/base/views/item_rep_collection_view.rb
- lib/nanoc/base/views/item_rep_view.rb
- lib/nanoc/base/views/item_with_reps_view.rb
- lib/nanoc/base/views/item_without_reps_view.rb
- lib/nanoc/base/views/layout_collection_view.rb
- lib/nanoc/base/views/layout_view.rb
- lib/nanoc/base/views/mixins/document_view_mixin.rb
- lib/nanoc/base/views/mixins/mutable_document_view_mixin.rb
- lib/nanoc/base/views/mixins/with_reps_view_mixin.rb
- lib/nanoc/base/views/mutable_config_view.rb
- lib/nanoc/base/views/mutable_identifiable_collection_view.rb
- lib/nanoc/base/views/mutable_item_collection_view.rb
- lib/nanoc/base/views/mutable_item_view.rb
- lib/nanoc/base/views/mutable_layout_collection_view.rb
- lib/nanoc/base/views/mutable_layout_view.rb
- lib/nanoc/base/views/post_compile_item_collection_view.rb
- lib/nanoc/base/views/post_compile_item_view.rb
- lib/nanoc/base/views/site_view.rb
- lib/nanoc/base/views/view.rb
- lib/nanoc/base/views/view_context.rb
- lib/nanoc/cli.rb
- lib/nanoc/cli/ansi_string_colorizer.rb
- lib/nanoc/cli/cleaning_stream.rb
- lib/nanoc/cli/command_runner.rb
- lib/nanoc/cli/commands/check.rb
- lib/nanoc/cli/commands/compile.rb
- lib/nanoc/cli/commands/create-site.rb
- lib/nanoc/cli/commands/deploy.rb
- lib/nanoc/cli/commands/nanoc.rb
- lib/nanoc/cli/commands/prune.rb
- lib/nanoc/cli/commands/shell.rb
- lib/nanoc/cli/commands/show-data.rb
- lib/nanoc/cli/commands/show-plugins.rb
- lib/nanoc/cli/commands/show-rules.rb
- lib/nanoc/cli/commands/view.rb
- lib/nanoc/cli/error_handler.rb
- lib/nanoc/cli/logger.rb
- lib/nanoc/cli/stream_cleaners.rb
- lib/nanoc/cli/stream_cleaners/abstract.rb
- lib/nanoc/cli/stream_cleaners/ansi_colors.rb
- lib/nanoc/cli/stream_cleaners/utf8.rb
- lib/nanoc/data_sources.rb
- lib/nanoc/data_sources/filesystem.rb
- lib/nanoc/data_sources/filesystem_unified.rb
- lib/nanoc/extra.rb
- lib/nanoc/extra/checking.rb
- lib/nanoc/extra/checking/check.rb
- lib/nanoc/extra/checking/checks.rb
- lib/nanoc/extra/checking/checks/css.rb
- lib/nanoc/extra/checking/checks/external_links.rb
- lib/nanoc/extra/checking/checks/html.rb
- lib/nanoc/extra/checking/checks/internal_links.rb
- lib/nanoc/extra/checking/checks/mixed_content.rb
- lib/nanoc/extra/checking/checks/stale.rb
- lib/nanoc/extra/checking/dsl.rb
- lib/nanoc/extra/checking/issue.rb
- lib/nanoc/extra/checking/runner.rb
- lib/nanoc/extra/core_ext.rb
- lib/nanoc/extra/core_ext/pathname.rb
- lib/nanoc/extra/core_ext/time.rb
- lib/nanoc/extra/deployer.rb
- lib/nanoc/extra/deployers.rb
- lib/nanoc/extra/deployers/fog.rb
- lib/nanoc/extra/deployers/rsync.rb
- lib/nanoc/extra/filesystem_tools.rb
- lib/nanoc/extra/jruby_nokogiri_warner.rb
- lib/nanoc/extra/link_collector.rb
- lib/nanoc/extra/piper.rb
- lib/nanoc/extra/pruner.rb
- lib/nanoc/filters.rb
- lib/nanoc/filters/asciidoc.rb
- lib/nanoc/filters/bluecloth.rb
- lib/nanoc/filters/coffeescript.rb
- lib/nanoc/filters/colorize_syntax.rb
- lib/nanoc/filters/erb.rb
- lib/nanoc/filters/erubis.rb
- lib/nanoc/filters/haml.rb
- lib/nanoc/filters/handlebars.rb
- lib/nanoc/filters/kramdown.rb
- lib/nanoc/filters/less.rb
- lib/nanoc/filters/markaby.rb
- lib/nanoc/filters/maruku.rb
- lib/nanoc/filters/mustache.rb
- lib/nanoc/filters/pandoc.rb
- lib/nanoc/filters/rainpress.rb
- lib/nanoc/filters/rdiscount.rb
- lib/nanoc/filters/rdoc.rb
- lib/nanoc/filters/redcarpet.rb
- lib/nanoc/filters/redcloth.rb
- lib/nanoc/filters/relativize_paths.rb
- lib/nanoc/filters/rubypants.rb
- lib/nanoc/filters/sass.rb
- lib/nanoc/filters/sass/sass_filesystem_importer.rb
- lib/nanoc/filters/slim.rb
- lib/nanoc/filters/typogruby.rb
- lib/nanoc/filters/uglify_js.rb
- lib/nanoc/filters/xsl.rb
- lib/nanoc/filters/yui_compressor.rb
- lib/nanoc/helpers.rb
- lib/nanoc/helpers/blogging.rb
- lib/nanoc/helpers/breadcrumbs.rb
- lib/nanoc/helpers/capturing.rb
- lib/nanoc/helpers/filtering.rb
- lib/nanoc/helpers/html_escape.rb
- lib/nanoc/helpers/link_to.rb
- lib/nanoc/helpers/rendering.rb
- lib/nanoc/helpers/tagging.rb
- lib/nanoc/helpers/text.rb
- lib/nanoc/helpers/xml_sitemap.rb
- lib/nanoc/rule_dsl.rb
- lib/nanoc/rule_dsl/action_provider.rb
- lib/nanoc/rule_dsl/compiler_dsl.rb
- lib/nanoc/rule_dsl/recording_executor.rb
- lib/nanoc/rule_dsl/rule.rb
- lib/nanoc/rule_dsl/rule_context.rb
- lib/nanoc/rule_dsl/rule_memory_calculator.rb
- lib/nanoc/rule_dsl/rules_collection.rb
- lib/nanoc/rule_dsl/rules_loader.rb
- lib/nanoc/version.rb
- nanoc.gemspec
- tasks/doc.rake
- tasks/rubocop.rake
- tasks/test.rake
- test/base/core_ext/array_spec.rb
- test/base/core_ext/hash_spec.rb
- test/base/core_ext/pathname_spec.rb
- test/base/core_ext/string_spec.rb
- test/base/temp_filename_factory_spec.rb
- test/base/test_checksum_store.rb
- test/base/test_code_snippet.rb
- test/base/test_compiler.rb
- test/base/test_context.rb
- test/base/test_data_source.rb
- test/base/test_dependency_tracker.rb
- test/base/test_directed_graph.rb
- test/base/test_filter.rb
- test/base/test_item.rb
- test/base/test_item_array.rb
- test/base/test_item_rep.rb
- test/base/test_layout.rb
- test/base/test_memoization.rb
- test/base/test_notification_center.rb
- test/base/test_outdatedness_checker.rb
- test/base/test_plugin.rb
- test/base/test_site.rb
- test/base/test_store.rb
- test/cli/commands/test_check.rb
- test/cli/commands/test_compile.rb
- test/cli/commands/test_create_site.rb
- test/cli/commands/test_deploy.rb
- test/cli/commands/test_help.rb
- test/cli/commands/test_info.rb
- test/cli/commands/test_prune.rb
- test/cli/test_cleaning_stream.rb
- test/cli/test_cli.rb
- test/cli/test_error_handler.rb
- test/cli/test_logger.rb
- test/data_sources/test_filesystem.rb
- test/data_sources/test_filesystem_unified.rb
- test/extra/checking/checks/test_css.rb
- test/extra/checking/checks/test_external_links.rb
- test/extra/checking/checks/test_html.rb
- test/extra/checking/checks/test_internal_links.rb
- test/extra/checking/checks/test_mixed_content.rb
- test/extra/checking/checks/test_stale.rb
- test/extra/checking/test_check.rb
- test/extra/checking/test_dsl.rb
- test/extra/checking/test_runner.rb
- test/extra/core_ext/test_pathname.rb
- test/extra/core_ext/test_time.rb
- test/extra/deployers/test_fog.rb
- test/extra/deployers/test_rsync.rb
- test/extra/test_filesystem_tools.rb
- test/extra/test_link_collector.rb
- test/extra/test_piper.rb
- test/filters/test_asciidoc.rb
- test/filters/test_bluecloth.rb
- test/filters/test_coffeescript.rb
- test/filters/test_colorize_syntax.rb
- test/filters/test_erb.rb
- test/filters/test_erubis.rb
- test/filters/test_haml.rb
- test/filters/test_handlebars.rb
- test/filters/test_kramdown.rb
- test/filters/test_less.rb
- test/filters/test_markaby.rb
- test/filters/test_maruku.rb
- test/filters/test_mustache.rb
- test/filters/test_pandoc.rb
- test/filters/test_rainpress.rb
- test/filters/test_rdiscount.rb
- test/filters/test_rdoc.rb
- test/filters/test_redcarpet.rb
- test/filters/test_redcloth.rb
- test/filters/test_relativize_paths.rb
- test/filters/test_rubypants.rb
- test/filters/test_sass.rb
- test/filters/test_slim.rb
- test/filters/test_typogruby.rb
- test/filters/test_uglify_js.rb
- test/filters/test_xsl.rb
- test/filters/test_yui_compressor.rb
- test/fixtures/vcr_cassettes/css_run_error.yml
- test/fixtures/vcr_cassettes/css_run_ok.yml
- test/fixtures/vcr_cassettes/css_run_parse_error.yml
- test/fixtures/vcr_cassettes/html_run_error.yml
- test/fixtures/vcr_cassettes/html_run_ok.yml
- test/helper.rb
- test/helpers/test_blogging.rb
- test/helpers/test_breadcrumbs.rb
- test/helpers/test_capturing.rb
- test/helpers/test_filtering.rb
- test/helpers/test_html_escape.rb
- test/helpers/test_link_to.rb
- test/helpers/test_rendering.rb
- test/helpers/test_tagging.rb
- test/helpers/test_text.rb
- test/helpers/test_xml_sitemap.rb
- test/rule_dsl/test_action_provider.rb
- test/rule_dsl/test_compiler_dsl.rb
- test/rule_dsl/test_rule.rb
- test/rule_dsl/test_rules_collection.rb
- test/test_gem.rb
homepage: http://nanoc.ws/
licenses:
- MIT
metadata: {}
post_install_message:
rdoc_options:
- "--main"
- README.md
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 2.1.0
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
requirements: []
rubyforge_project:
rubygems_version: 2.5.2
signing_key:
specification_version: 4
summary: A static-site generator with a focus on flexibility.
test_files: []
has_rdoc:
nanoc-4.1.4/test/0000755000004100000410000000000012665031555013635 5ustar www-datawww-datananoc-4.1.4/test/base/0000755000004100000410000000000012665031555014547 5ustar www-datawww-datananoc-4.1.4/test/base/test_outdatedness_checker.rb0000644000004100000410000003553712665031555022336 0ustar www-datawww-dataclass Nanoc::Int::OutdatednessCheckerTest < Nanoc::TestCase
def test_not_outdated
# Compile once
with_site(name: 'foo') do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
File.open('lib/stuff.rb', 'w') { |io| io.write('$foo = 123') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Check
with_site(name: 'foo') do |site|
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
assert_nil outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_item_checksum_nil
# Compile once
with_site(name: 'foo') do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
File.open('lib/stuff.rb', 'w') { |io| io.write('$foo = 123') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Delete checksums
with_site(name: 'foo') do |_site|
FileUtils.rm('tmp/checksums')
end
# Check
with_site(name: 'foo') do |site|
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::NotEnoughData, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_compiled_file_doesnt_exist
# Compile once
with_site(name: 'foo') do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
File.open('lib/stuff.rb', 'w') { |io| io.write('$foo = 123') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Delete old item
FileUtils.cd('foo') do
FileUtils.rm_rf('output/index.html')
end
# Check
with_site(name: 'foo') do |site|
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::NotWritten, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_item_checksum_is_different
# Compile once
with_site(name: 'foo') do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
File.open('content/new.html', 'w') { |io| io.write('o hello too') }
File.open('lib/stuff.rb', 'w') { |io| io.write('$foo = 123') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Create new item
FileUtils.cd('foo') do
File.open('content/new.html', 'w') { |io| io.write('o hello DIFFERENT!!!') }
end
# Check
with_site(name: 'foo') do |site|
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/new/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::SourceModified, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_dependent_layout_outdated
# Compile once
with_site(name: 'foo', compilation_rule_content: 'layout "/default/"', has_layout: true) do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
File.open('layouts/default.html', 'w') { |io| io.write('!!! <%= yield %> !!!') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Change layout
FileUtils.cd('foo') do
File.open('layouts/default.html', 'w') { |io| io.write('!!! <%= yield %> !!! different') }
end
# Check
with_site(name: 'foo') do |site|
# FIXME: ugly fugly hack
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::DependenciesOutdated, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_dependent_item_outdated
# Compile once
with_site(name: 'foo', compilation_rule_content: 'filter :erb') do |site|
File.open('content/a.html', 'w') do |io|
io.write('<%= @items.find { |i| i.identifier == "/b/" }.compiled_content %>')
end
File.open('content/b.html', 'w') do |io|
io.write('stuff')
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Change item
FileUtils.cd('foo') do
File.open('content/b.html', 'w') do |io|
io.write('stuff different!!!')
end
end
# Check
with_site(name: 'foo') do |site|
# FIXME: ugly fugly hack
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/a/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::DependenciesOutdated, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_dependent_item_outdated_chained
# Compile once
with_site(name: 'foo', compilation_rule_content: 'filter :erb') do |site|
File.open('content/a.html', 'w') do |io|
io.write('<%= @items.find { |i| i.identifier == "/b/" }.compiled_content %> aaa')
end
File.open('content/b.html', 'w') do |io|
io.write('<%= @items.find { |i| i.identifier == "/c/" }.compiled_content %> bbb')
end
File.open('content/c.html', 'w') do |io|
io.write('stuff')
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Change item
FileUtils.cd('foo') do
File.open('content/c.html', 'w') do |io|
io.write('stuff different!!!')
end
end
# Check
with_site(name: 'foo') do |site|
# FIXME: ugly fugly hack
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/a/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::DependenciesOutdated, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_dependent_item_removed
# Compile once
with_site(name: 'foo', compilation_rule_content: 'filter :erb') do |site|
File.open('content/a.html', 'w') do |io|
io.write('<% @items.select { |i| i.identifier != @item.identifier }.each do |i| %>')
io.write(' <%= i.compiled_content %>')
io.write('<% end %>')
end
File.open('content/b.html', 'w') do |io|
io.write('stuff')
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Delete item
FileUtils.cd('foo') do
FileUtils.rm_rf('content/b.html')
end
# Check
with_site(name: 'foo') do |site|
# FIXME: ugly fugly hack
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/a/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::DependenciesOutdated, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_dependent_item_added
# Compile once
with_site(name: 'foo', compilation_rule_content: 'filter :erb') do |site|
File.open('content/a.html', 'w') do |io|
io.write('<% @items.select { |i| i.identifier != @item.identifier }.each do |i| %>')
io.write(' <%= i.compiled_content %>')
io.write('<% end %>')
end
File.open('content/b.html', 'w') do |io|
io.write('stuff')
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Add item
FileUtils.cd('foo') do
File.open('content/z.html', 'w') do |io|
io.write('moar stuff')
end
end
# Check
with_site(name: 'foo') do |site|
# FIXME: ugly fugly hack
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/a/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::DependenciesOutdated, outdatedness_checker.outdatedness_reason_for(rep)
end
end
# TODO: make sure outdatedness of non-outdated items is correct
def test_outdated_if_code_snippets_outdated
# Compile once
with_site(name: 'foo') do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Change code
FileUtils.cd('foo') do
File.open('lib/moo.rb', 'w') { |io| io.write('def moo ; puts "moo" ; end') }
end
# Check
with_site(name: 'foo') do |site|
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::CodeSnippetsModified, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_config_outdated
# Compile once
with_site(name: 'foo') do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Change code
FileUtils.cd('foo') do
File.open('nanoc.yaml', 'w') do |io|
io << 'awesome: true' << "\n"
io << 'string_pattern_type: legacy' << "\n"
io << 'data_sources:' << "\n"
io << ' -' << "\n"
io << ' type: filesystem' << "\n"
io << ' identifier_type: legacy' << "\n"
end
end
# Check
with_site(name: 'foo') do |site|
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::ConfigurationModified, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_not_outdated_if_irrelevant_rule_modified
# Compile once
with_site(name: 'foo') do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Change code
FileUtils.cd('foo') do
File.open('Rules', 'a') { |io| io.write('layout "/moo/", :haml') }
end
# Check
with_site(name: 'foo') do |site|
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
assert_nil outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_outdated_if_relevant_rule_modified
# Create site
with_site(name: 'foo') do |_site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
File.open('Rules', 'w') do |io|
io.write("compile '/' do\n")
io.write(" filter :erb\n")
io.write("end\n")
io.write("\n")
io.write("route '/' do\n")
io.write(" '/index.html'\n")
io.write("end\n")
end
end
# Compile once
FileUtils.cd('foo') do
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Modify rules
FileUtils.cd('foo') do
File.open('Rules', 'w') do |io|
io.write("compile '/' do\n")
io.write("end\n")
io.write("\n")
io.write("route '/' do\n")
io.write(" '/index.html'\n")
io.write("end\n")
end
end
# Check
FileUtils.cd('foo') do
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compiler.build_reps
site.compiler.load_stores
outdatedness_checker = site.compiler.send :outdatedness_checker
rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
assert_equal ::Nanoc::Int::OutdatednessReasons::RulesModified, outdatedness_checker.outdatedness_reason_for(rep)
end
end
def test_items_in_rules_should_not_cause_outdatedness
# Create site
with_site(name: 'foo') do |_site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
File.open('Rules', 'w') do |io|
io.write("compile '/' do\n")
io.write(" filter :erb, :stuff => @items\n")
io.write("end\n")
io.write("\n")
io.write("route '/' do\n")
io.write(" '/index.html'\n")
io.write("end\n")
end
end
# Compile
FileUtils.cd('foo') do
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Assert not outdated
FileUtils.cd('foo') do
site = Nanoc::Int::SiteLoader.new.new_from_cwd
outdatedness_checker = site.compiler.outdatedness_checker
site.items.each do |item|
refute outdatedness_checker.outdated?(item), 'item should not be outdated'
end
end
end
def test_non_serializable_parameters_in_rules_should_be_allowed
# Create site
with_site(name: 'foo') do |_site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
File.open('Rules', 'w') do |io|
io.write("compile '/' do\n")
io.write(" c = Class.new {}\n")
io.write(" def c.inspect ; 'I am so classy' ; end\n")
io.write(" filter :erb, :stuff => c, :more => 123\n")
io.write("end\n")
io.write("\n")
io.write("route '/' do\n")
io.write(" '/index.html'\n")
io.write("end\n")
end
end
# Compile
FileUtils.cd('foo') do
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
# Assert not outdated
FileUtils.cd('foo') do
site = Nanoc::Int::SiteLoader.new.new_from_cwd
outdatedness_checker = site.compiler.outdatedness_checker
site.items.each do |item|
refute outdatedness_checker.outdated?(item), 'item should not be outdated'
end
end
end
end
nanoc-4.1.4/test/base/test_item_array.rb0000644000004100000410000000346112665031555020273 0ustar www-datawww-dataclass Nanoc::Int::IdentifiableCollectionTest < Nanoc::TestCase
def setup
super
@one = Nanoc::Int::Item.new('Item One', {}, '/one/')
@two = Nanoc::Int::Item.new('Item Two', {}, '/two/')
@items = Nanoc::Int::IdentifiableCollection.new({})
@items << @one
@items << @two
end
def test_change_item_identifier
assert_equal @one, @items['/one/']
assert_nil @items['/foo/']
@one.identifier = '/foo/'
assert_nil @items['/one/']
assert_equal @one, @items['/foo/']
end
def test_enumerable
assert_equal @one, @items.find { |i| i.identifier == '/one/' }
end
def test_brackets_with_glob
@items = Nanoc::Int::IdentifiableCollection.new({ string_pattern_type: 'glob' })
@items << @one
@items << @two
assert_equal @one, @items['/on*/']
assert_equal @two, @items['/*wo/']
end
def test_brackets_with_identifier
assert_equal @one, @items['/one/']
assert_equal @two, @items['/two/']
assert_nil @items['/max-payne/']
end
def test_brackets_with_malformed_identifier
assert_nil @items['one/']
assert_nil @items['/one']
assert_nil @items['one']
assert_nil @items['//one/']
end
def test_brackets_frozen
@items.freeze
assert_equal @one, @items['/one/']
assert_nil @items['/tenthousand/']
end
def test_regex
foo = Nanoc::Int::Item.new('Item Foo', {}, '/foo/')
@items << foo
assert_equal @one, @items[/n/]
assert_equal @two, @items[%r{o/}] # not foo
end
def test_less_than_less_than
assert_nil @items['/foo/']
foo = Nanoc::Int::Item.new('Item Foo', {}, '/foo/')
@items << foo
assert_equal foo, @items['/foo/']
end
def test_concat
new_item = Nanoc::Int::Item.new('New item', {}, '/new/')
@items.concat([new_item])
assert_equal new_item, @items['/new/']
end
end
nanoc-4.1.4/test/base/core_ext/0000755000004100000410000000000012665031555016357 5ustar www-datawww-datananoc-4.1.4/test/base/core_ext/pathname_spec.rb0000644000004100000410000000000012665031555021501 0ustar www-datawww-datananoc-4.1.4/test/base/core_ext/hash_spec.rb0000644000004100000410000000206712665031555020646 0ustar www-datawww-datadescribe 'Hash#__nanoc_symbolize_keys_recursively' do
it 'should convert keys to symbols' do
hash_old = { 'foo' => 'bar' }
hash_new = { foo: 'bar' }
hash_old.__nanoc_symbolize_keys_recursively.must_equal hash_new
end
it 'should not require string keys' do
hash_old = { Time.now => 'abc' }
hash_new = hash_old
hash_old.__nanoc_symbolize_keys_recursively.must_equal hash_new
end
end
describe 'Hash#__nanoc_freeze_recursively' do
include Nanoc::TestHelpers
it 'should prevent first-level elements from being modified' do
hash = { a: { b: :c } }
hash.__nanoc_freeze_recursively
assert_raises_frozen_error do
hash[:a] = 123
end
end
it 'should prevent second-level elements from being modified' do
hash = { a: { b: :c } }
hash.__nanoc_freeze_recursively
assert_raises_frozen_error do
hash[:a][:b] = 123
end
end
it 'should not freeze infinitely' do
a = {}
a[:x] = a
a.__nanoc_freeze_recursively
assert a.frozen?
assert a[:x].frozen?
assert_equal a, a[:x]
end
end
nanoc-4.1.4/test/base/core_ext/string_spec.rb0000644000004100000410000000120612665031555021223 0ustar www-datawww-datadescribe 'String#__nanoc_cleaned_identifier' do
it 'should not convert already clean paths' do
'/foo/bar/'.__nanoc_cleaned_identifier.must_equal '/foo/bar/'
end
it 'should prepend slash if necessary' do
'foo/bar/'.__nanoc_cleaned_identifier.must_equal '/foo/bar/'
end
it 'should append slash if necessary' do
'/foo/bar'.__nanoc_cleaned_identifier.must_equal '/foo/bar/'
end
it 'should remove double slashes at start' do
'//foo/bar/'.__nanoc_cleaned_identifier.must_equal '/foo/bar/'
end
it 'should remove double slashes at end' do
'/foo/bar//'.__nanoc_cleaned_identifier.must_equal '/foo/bar/'
end
end
nanoc-4.1.4/test/base/core_ext/array_spec.rb0000644000004100000410000000170512665031555021037 0ustar www-datawww-datadescribe 'Array#__nanoc_symbolize_keys_recursively' do
it 'should convert keys to symbols' do
array_old = [:abc, 'xyz', { 'foo' => 'bar', :baz => :qux }]
array_new = [:abc, 'xyz', { foo: 'bar', baz: :qux }]
array_old.__nanoc_symbolize_keys_recursively.must_equal array_new
end
end
describe 'Array#__nanoc_freeze_recursively' do
include Nanoc::TestHelpers
it 'should prevent first-level elements from being modified' do
array = [:a, [:b, :c], :d]
array.__nanoc_freeze_recursively
assert_raises_frozen_error do
array[0] = 123
end
end
it 'should prevent second-level elements from being modified' do
array = [:a, [:b, :c], :d]
array.__nanoc_freeze_recursively
assert_raises_frozen_error do
array[1][0] = 123
end
end
it 'should not freeze infinitely' do
a = []
a << a
a.__nanoc_freeze_recursively
assert a.frozen?
assert a[0].frozen?
assert_equal a, a[0]
end
end
nanoc-4.1.4/test/base/test_dependency_tracker.rb0000644000004100000410000001707012665031555021771 0ustar www-datawww-dataclass Nanoc::Int::DependencyTrackerTest < Nanoc::TestCase
def test_initialize
# Mock items
items = [mock, mock]
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Verify no dependencies yet
assert_empty store.objects_causing_outdatedness_of(items[0])
assert_empty store.objects_causing_outdatedness_of(items[1])
end
def test_record_dependency
# Mock items
items = [mock, mock]
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[1])
# Verify dependencies
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
end
def test_record_dependency_no_self
# Mock items
items = [mock, mock]
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[0])
store.record_dependency(items[0], items[1])
# Verify dependencies
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
end
def test_record_dependency_no_doubles
# Mock items
items = [mock, mock]
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[1])
store.record_dependency(items[0], items[1])
store.record_dependency(items[0], items[1])
# Verify dependencies
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
end
def test_objects_causing_outdatedness_of
# Mock items
items = [mock, mock, mock]
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[1])
store.record_dependency(items[1], items[2])
# Verify dependencies
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
end
def test_objects_outdated_due_to
# Mock items
items = [mock, mock, mock]
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[1])
store.record_dependency(items[1], items[2])
# Verify dependencies
assert_contains_exactly [items[0]], store.objects_outdated_due_to(items[1])
end
def test_start_and_stop
# Mock items
items = [mock, mock]
# Create
store = Nanoc::Int::DependencyStore.new(items)
tracker = Nanoc::Int::DependencyTracker.new(store)
# Start, do something and stop
tracker.run do
Nanoc::Int::NotificationCenter.post(:visit_started, items[0])
Nanoc::Int::NotificationCenter.post(:visit_started, items[1])
Nanoc::Int::NotificationCenter.post(:visit_ended, items[1])
Nanoc::Int::NotificationCenter.post(:visit_ended, items[0])
end
# Verify dependencies
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
assert_empty store.objects_causing_outdatedness_of(items[1])
end
def test_store_graph_and_load_graph_simple
# Mock items
items = [mock('0'), mock('1'), mock('2'), mock('3')]
items.each { |i| i.stubs(:type).returns(:item) }
items[0].stubs(:reference).returns([:item, '/aaa/'])
items[1].stubs(:reference).returns([:item, '/bbb/'])
items[2].stubs(:reference).returns([:item, '/ccc/'])
items[3].stubs(:reference).returns([:item, '/ddd/'])
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[1])
store.record_dependency(items[1], items[2])
store.record_dependency(items[1], items[3])
# Store
store.store
assert File.file?(store.filename)
# Re-create
store = Nanoc::Int::DependencyStore.new(items)
# Load
store.load
# Check loaded graph
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
assert_contains_exactly [items[2], items[3]], store.objects_causing_outdatedness_of(items[1])
assert_empty store.objects_causing_outdatedness_of(items[2])
assert_empty store.objects_causing_outdatedness_of(items[3])
end
def test_store_graph_and_load_graph_with_removed_items
# Mock items
items = [mock('0'), mock('1'), mock('2'), mock('3')]
items.each { |i| i.stubs(:type).returns(:item) }
items[0].stubs(:reference).returns([:item, '/aaa/'])
items[1].stubs(:reference).returns([:item, '/bbb/'])
items[2].stubs(:reference).returns([:item, '/ccc/'])
items[3].stubs(:reference).returns([:item, '/ddd/'])
# Create new and old lists
old_items = [items[0], items[1], items[2], items[3]]
new_items = [items[0], items[1], items[2]]
# Create
store = Nanoc::Int::DependencyStore.new(old_items)
# Record some dependencies
store.record_dependency(old_items[0], old_items[1])
store.record_dependency(old_items[1], old_items[2])
store.record_dependency(old_items[1], old_items[3])
# Store
store.store
assert File.file?(store.filename)
# Re-create
store = Nanoc::Int::DependencyStore.new(new_items)
# Load
store.load
# Check loaded graph
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
assert_contains_exactly [items[2], nil], store.objects_causing_outdatedness_of(items[1])
assert_empty store.objects_causing_outdatedness_of(items[2])
end
def test_store_graph_with_nils_in_dst
# Mock items
items = [mock('0'), mock('1'), mock('2')]
items.each { |i| i.stubs(:type).returns(:item) }
items[0].stubs(:reference).returns([:item, '/aaa/'])
items[1].stubs(:reference).returns([:item, '/bbb/'])
items[2].stubs(:reference).returns([:item, '/ccc/'])
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[1])
store.record_dependency(items[1], nil)
# Store
store.store
assert File.file?(store.filename)
# Re-create
store = Nanoc::Int::DependencyStore.new(items)
# Load
store.load
# Check loaded graph
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
assert_contains_exactly [nil], store.objects_causing_outdatedness_of(items[1])
end
def test_store_graph_with_nils_in_src
# Mock items
items = [mock('0'), mock('1'), mock('2')]
items.each { |i| i.stubs(:type).returns(:item) }
items[0].stubs(:reference).returns([:item, '/aaa/'])
items[1].stubs(:reference).returns([:item, '/bbb/'])
items[2].stubs(:reference).returns([:item, '/ccc/'])
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[1])
store.record_dependency(nil, items[2])
# Store
store.store
assert File.file?(store.filename)
# Re-create
store = Nanoc::Int::DependencyStore.new(items)
# Load
store.load
# Check loaded graph
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
assert_empty store.objects_causing_outdatedness_of(items[1])
end
def test_forget_dependencies_for
# Mock items
items = [mock, mock, mock]
# Create
store = Nanoc::Int::DependencyStore.new(items)
# Record some dependencies
store.record_dependency(items[0], items[1])
store.record_dependency(items[1], items[2])
assert_contains_exactly [items[1]], store.objects_causing_outdatedness_of(items[0])
# Forget dependencies
store.forget_dependencies_for(items[0])
assert_empty store.objects_causing_outdatedness_of(items[0])
end
end
nanoc-4.1.4/test/base/test_plugin.rb0000644000004100000410000000112612665031555017431 0ustar www-datawww-dataclass Nanoc::PluginTest < Nanoc::TestCase
class SampleFilter < Nanoc::Filter
identifier :_plugin_test_sample_filter
end
def test_named
# Find existant filter
filter = Nanoc::Filter.named(:erb)
assert(!filter.nil?)
# Find non-existant filter
filter = Nanoc::Filter.named(:lksdaffhdlkashlgkskahf)
assert(filter.nil?)
end
def test_register
SampleFilter.send(:identifier, :_plugin_test_sample_filter)
registry = Nanoc::Int::PluginRegistry.instance
filter = registry.find(Nanoc::Filter, :_plugin_test_sample_filter)
refute_nil filter
end
end
nanoc-4.1.4/test/base/test_directed_graph.rb0000644000004100000410000002134012665031555021077 0ustar www-datawww-dataclass Nanoc::Int::DirectedGraphTest < Nanoc::TestCase
def test_direct_predecessors
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
assert_equal [], graph.direct_predecessors_of(1)
assert_equal [1], graph.direct_predecessors_of(2)
assert_equal [2], graph.direct_predecessors_of(3)
end
def test_predecessors
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
assert_equal [], graph.predecessors_of(1).sort
assert_equal [1], graph.predecessors_of(2).sort
assert_equal [1, 2], graph.predecessors_of(3).sort
end
def test_direct_successors
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
assert_equal [2], graph.direct_successors_of(1)
assert_equal [3], graph.direct_successors_of(2)
assert_equal [], graph.direct_successors_of(3)
end
def test_successors
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
assert_equal [2, 3], graph.successors_of(1).sort
assert_equal [3], graph.successors_of(2).sort
assert_equal [], graph.successors_of(3).sort
end
def test_edges
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
assert_equal [[0, 1], [1, 2]], graph.edges.sort
end
def test_edges_with_new_vertices
graph = Nanoc::Int::DirectedGraph.new([1])
assert_equal [1], graph.vertices
graph.add_edge(1, 2)
assert_equal [1, 2], graph.vertices
graph.add_edge(3, 2)
assert_equal [1, 2, 3], graph.vertices
assert_equal [[0, 1], [2, 1]], graph.edges.sort
end
def test_add_edge
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
assert_equal [], graph.successors_of(1)
assert_equal [], graph.predecessors_of(2)
graph.add_edge(1, 2)
assert_equal [2], graph.successors_of(1)
assert_equal [1], graph.predecessors_of(2)
end
def test_add_edge_with_new_vertices
graph = Nanoc::Int::DirectedGraph.new([1])
graph.add_edge(1, 2)
graph.add_edge(3, 2)
assert graph.vertices.include?(2)
assert graph.vertices.include?(3)
end
def test_delete_edge
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
assert_equal [2], graph.successors_of(1)
assert_equal [1], graph.predecessors_of(2)
graph.delete_edge(1, 2)
assert_equal [], graph.successors_of(1)
assert_equal [], graph.predecessors_of(2)
end
def test_delete_edges_from
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 1)
graph.add_edge(2, 3)
graph.add_edge(3, 2)
graph.add_edge(1, 3)
graph.add_edge(3, 1)
assert_equal [2, 3], graph.direct_predecessors_of(1).sort
assert_equal [2, 3], graph.direct_successors_of(1).sort
assert_equal [1, 3], graph.direct_predecessors_of(2).sort
assert_equal [1, 3], graph.direct_successors_of(2).sort
assert_equal [1, 2], graph.direct_predecessors_of(3).sort
assert_equal [1, 2], graph.direct_successors_of(3).sort
assert_equal Set.new([]), graph.roots
graph.delete_edges_from(1)
assert_equal [2, 3], graph.direct_predecessors_of(1).sort
assert_equal [], graph.direct_successors_of(1).sort
assert_equal [3], graph.direct_predecessors_of(2).sort
assert_equal [1, 3], graph.direct_successors_of(2).sort
assert_equal [2], graph.direct_predecessors_of(3).sort
assert_equal [1, 2], graph.direct_successors_of(3).sort
assert_equal Set.new([]), graph.roots
graph.delete_edges_from(2)
assert_equal [3], graph.direct_predecessors_of(1).sort
assert_equal [], graph.direct_successors_of(1).sort
assert_equal [3], graph.direct_predecessors_of(2).sort
assert_equal [], graph.direct_successors_of(2).sort
assert_equal [], graph.direct_predecessors_of(3).sort
assert_equal [1, 2], graph.direct_successors_of(3).sort
assert_equal Set.new([3]), graph.roots
end
def test_delete_edges_to
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 1)
graph.add_edge(2, 3)
graph.add_edge(3, 2)
graph.add_edge(1, 3)
graph.add_edge(3, 1)
assert_equal [2, 3], graph.direct_predecessors_of(1).sort
assert_equal [2, 3], graph.direct_successors_of(1).sort
assert_equal [1, 3], graph.direct_predecessors_of(2).sort
assert_equal [1, 3], graph.direct_successors_of(2).sort
assert_equal [1, 2], graph.direct_predecessors_of(3).sort
assert_equal [1, 2], graph.direct_successors_of(3).sort
assert_equal Set.new([]), graph.roots
graph.delete_edges_to(1)
assert_equal [], graph.direct_predecessors_of(1).sort
assert_equal [2, 3], graph.direct_successors_of(1).sort
assert_equal [1, 3], graph.direct_predecessors_of(2).sort
assert_equal [3], graph.direct_successors_of(2).sort
assert_equal [1, 2], graph.direct_predecessors_of(3).sort
assert_equal [2], graph.direct_successors_of(3).sort
assert_equal Set.new([1]), graph.roots
graph.delete_edges_to(2)
assert_equal [], graph.direct_predecessors_of(1).sort
assert_equal [3], graph.direct_successors_of(1).sort
assert_equal [], graph.direct_predecessors_of(2).sort
assert_equal [3], graph.direct_successors_of(2).sort
assert_equal [1, 2], graph.direct_predecessors_of(3).sort
assert_equal [], graph.direct_successors_of(3).sort
assert_equal Set.new([1, 2]), graph.roots
end
def test_delete_vertex
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 1)
graph.add_edge(2, 3)
graph.add_edge(3, 2)
graph.add_edge(1, 3)
graph.add_edge(3, 1)
graph.delete_vertex(2)
assert_equal [3], graph.direct_predecessors_of(1).sort
assert_equal [3], graph.direct_successors_of(1).sort
assert_equal [1], graph.direct_predecessors_of(3).sort
assert_equal [1], graph.direct_successors_of(3).sort
assert_equal Set.new([]), graph.roots
end
def test_delete_vertex_resulting_roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
assert_equal Set.new([1, 2, 3]), graph.roots
graph.add_edge(1, 2)
graph.add_edge(2, 3)
assert_equal Set.new([1]), graph.roots
graph.delete_vertex(2)
assert_equal Set.new([1, 3]), graph.roots
end
def test_should_return_empty_array_for_nonexistant_vertices
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
assert_equal [], graph.direct_predecessors_of(4)
assert_equal [], graph.predecessors_of(4)
assert_equal [], graph.direct_successors_of(4)
assert_equal [], graph.successors_of(4)
end
def test_roots_after_init
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
assert_equal Set.new([1, 2, 3]), graph.roots
end
def test_roots_after_adding_edge
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
assert_equal Set.new([1, 3]), graph.roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 3)
assert_equal Set.new([1, 2]), graph.roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(2, 1)
assert_equal Set.new([2, 3]), graph.roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
assert_equal Set.new([1]), graph.roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
graph.add_edge(3, 1)
assert_equal Set.new([]), graph.roots
end
def test_roots_after_removing_edge
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.delete_edge(1, 2)
assert_equal Set.new([1, 2, 3]), graph.roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 3)
assert_equal Set.new([1, 2]), graph.roots
graph.delete_edge(1, 2) # no such edge
assert_equal Set.new([1, 2]), graph.roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(2, 1)
graph.delete_edge(2, 1)
assert_equal Set.new([1, 2, 3]), graph.roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
graph.delete_edge(1, 2)
assert_equal Set.new([1, 2]), graph.roots
graph.delete_edge(2, 3)
assert_equal Set.new([1, 2, 3]), graph.roots
graph = Nanoc::Int::DirectedGraph.new([1, 2, 3])
graph.add_edge(1, 2)
graph.add_edge(2, 3)
graph.add_edge(3, 1)
graph.delete_edge(1, 2)
assert_equal Set.new([2]), graph.roots
graph.delete_edge(2, 3)
assert_equal Set.new([2, 3]), graph.roots
graph.delete_edge(3, 1)
assert_equal Set.new([1, 2, 3]), graph.roots
end
def test_example
YARD.parse(LIB_DIR + '/nanoc/base/directed_graph.rb')
assert_examples_correct 'Nanoc::Int::DirectedGraph'
end
end
nanoc-4.1.4/test/base/test_notification_center.rb0000644000004100000410000000126212665031555022162 0ustar www-datawww-dataclass Nanoc::Int::NotificationCenterTest < Nanoc::TestCase
def test_post
# Set up notification
Nanoc::Int::NotificationCenter.on :ping_received, :test do
@ping_received = true
end
# Post
@ping_received = false
Nanoc::Int::NotificationCenter.post :ping_received
assert(@ping_received)
end
def test_remove
# Set up notification
Nanoc::Int::NotificationCenter.on :ping_received, :test do
@ping_received = true
end
# Remove observer
Nanoc::Int::NotificationCenter.remove :ping_received, :test
# Post
@ping_received = false
Nanoc::Int::NotificationCenter.post :ping_received
assert(!@ping_received)
end
end
nanoc-4.1.4/test/base/test_item_rep.rb0000644000004100000410000001040012665031555017732 0ustar www-datawww-dataclass Nanoc::Int::ItemRepTest < Nanoc::TestCase
def test_compiled_content_with_only_last_available
# Create rep
item = Nanoc::Int::Item.new(
'blah blah blah', {}, '/'
)
rep = Nanoc::Int::ItemRep.new(item, nil)
rep.snapshot_contents = {
last: Nanoc::Int::TextualContent.new('last content'),
}
rep.expects(:compiled?).returns(true)
# Check
assert_equal 'last content', rep.compiled_content
end
def test_compiled_content_with_pre_and_last_available
# Create rep
item = Nanoc::Int::Item.new(
'blah blah blah', {}, '/'
)
rep = Nanoc::Int::ItemRep.new(item, nil)
rep.snapshot_contents = {
pre: Nanoc::Int::TextualContent.new('pre content'),
last: Nanoc::Int::TextualContent.new('last content'),
}
rep.expects(:compiled?).returns(true)
# Check
assert_equal 'pre content', rep.compiled_content
end
def test_compiled_content_with_custom_snapshot
# Create rep
item = Nanoc::Int::Item.new(
'blah blah blah', {}, '/'
)
rep = Nanoc::Int::ItemRep.new(item, nil)
rep.snapshot_contents = {
pre: Nanoc::Int::TextualContent.new('pre content'),
last: Nanoc::Int::TextualContent.new('last content'),
}
rep.expects(:compiled?).returns(true)
# Check
assert_equal 'last content', rep.compiled_content(snapshot: :last)
end
def test_compiled_content_with_invalid_snapshot
# Create rep
item = Nanoc::Int::Item.new(
'blah blah blah', {}, '/'
)
rep = Nanoc::Int::ItemRep.new(item, nil)
rep.snapshot_contents = {
pre: Nanoc::Int::TextualContent.new('pre content'),
last: Nanoc::Int::TextualContent.new('last content'),
}
# Check
assert_raises Nanoc::Int::Errors::NoSuchSnapshot do
rep.compiled_content(snapshot: :klsjflkasdfl)
end
end
def test_compiled_content_with_uncompiled_content
# Create rep
item = Nanoc::Int::Item.new(
'blah blah', {}, '/'
)
rep = Nanoc::Int::ItemRep.new(item, nil)
rep.expects(:compiled?).returns(false)
# Check
assert_raises(Nanoc::Int::Errors::UnmetDependency) do
rep.compiled_content
end
end
def test_compiled_content_with_moving_pre_snapshot
# Create rep
item = Nanoc::Int::Item.new(
'blah blah', {}, '/'
)
rep = Nanoc::Int::ItemRep.new(item, nil)
rep.expects(:compiled?).returns(false)
rep.snapshot_contents = {
pre: Nanoc::Int::TextualContent.new('pre!'),
last: Nanoc::Int::TextualContent.new('last!'),
}
# Check
assert_raises(Nanoc::Int::Errors::UnmetDependency) do
rep.compiled_content(snapshot: :pre)
end
end
def test_compiled_content_with_non_moving_pre_snapshot
# Create rep
item = Nanoc::Int::Item.new(
'blah blah', {}, '/'
)
rep = Nanoc::Int::ItemRep.new(item, nil)
rep.expects(:compiled?).returns(false)
rep.snapshot_defs = [
Nanoc::Int::SnapshotDef.new(:pre, true),
]
rep.snapshot_contents = {
pre: Nanoc::Int::TextualContent.new('pre!'),
post: Nanoc::Int::TextualContent.new('post!'),
last: Nanoc::Int::TextualContent.new('last!'),
}
# Check
assert_equal 'pre!', rep.compiled_content(snapshot: :pre)
end
def test_compiled_content_with_multiple_pre_snapshots
# Create rep
item = Nanoc::Int::Item.new(
'blah blah', {}, '/'
)
rep = Nanoc::Int::ItemRep.new(item, nil)
rep.expects(:compiled?).returns(false)
rep.snapshot_defs = [
Nanoc::Int::SnapshotDef.new(:pre, false),
Nanoc::Int::SnapshotDef.new(:pre, true),
]
rep.snapshot_contents = {
pre: Nanoc::Int::TextualContent.new('pre!'),
post: Nanoc::Int::TextualContent.new('post!'),
last: Nanoc::Int::TextualContent.new('last!'),
}
# Check
assert_equal 'pre!', rep.compiled_content(snapshot: :pre)
end
def test_access_compiled_content_of_binary_item
content = Nanoc::Int::BinaryContent.new(File.expand_path('content/somefile.dat'))
item = Nanoc::Int::Item.new(content, {}, '/somefile/')
item_rep = Nanoc::Int::ItemRep.new(item, :foo)
assert_raises(Nanoc::Int::Errors::CannotGetCompiledContentOfBinaryItem) do
item_rep.compiled_content
end
end
private
def create_rep_for(item, name)
Nanoc::Int::ItemRep.new(item, name)
end
end
nanoc-4.1.4/test/base/test_memoization.rb0000644000004100000410000000141312665031555020465 0ustar www-datawww-dataclass Nanoc::Int::MemoizationTest < Nanoc::TestCase
class Sample1
extend Nanoc::Int::Memoization
def initialize(value)
@value = value
end
def run(n)
@value * 10 + n
end
memoize :run
end
class Sample2
extend Nanoc::Int::Memoization
def initialize(value)
@value = value
end
def run(n)
@value * 100 + n
end
memoize :run
end
def test_normal
sample1a = Sample1.new(10)
sample1b = Sample1.new(15)
sample2a = Sample2.new(20)
sample2b = Sample2.new(25)
3.times do
assert_equal 10 * 10 + 5, sample1a.run(5)
assert_equal 10 * 15 + 7, sample1b.run(7)
assert_equal 100 * 20 + 5, sample2a.run(5)
assert_equal 100 * 25 + 7, sample2b.run(7)
end
end
end
nanoc-4.1.4/test/base/test_layout.rb0000644000004100000410000000062212665031555017450 0ustar www-datawww-dataclass Nanoc::Int::LayoutTest < Nanoc::TestCase
def test_initialize
# Make sure attributes are cleaned
layout = Nanoc::Int::Layout.new('content', { 'foo' => 'bar' }, '/foo')
assert_equal({ foo: 'bar' }, layout.attributes)
end
def test_attributes
layout = Nanoc::Int::Layout.new('content', { 'foo' => 'bar' }, '/foo/')
assert_equal({ foo: 'bar' }, layout.attributes)
end
end
nanoc-4.1.4/test/base/test_site.rb0000644000004100000410000001025012665031555017075 0ustar www-datawww-dataclass Nanoc::Int::SiteTest < Nanoc::TestCase
def test_initialize_with_dir_without_config_yaml
assert_raises(Nanoc::Int::ConfigLoader::NoConfigFileFoundError) do
Nanoc::Int::SiteLoader.new.new_from_cwd
end
end
def test_initialize_with_dir_with_config_yaml
File.open('config.yaml', 'w') { |io| io.write('output_dir: public_html') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
assert_equal 'public_html', site.config[:output_dir]
end
def test_initialize_with_dir_with_nanoc_yaml
File.open('nanoc.yaml', 'w') { |io| io.write('output_dir: public_html') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
assert_equal 'public_html', site.config[:output_dir]
end
def test_initialize_with_config_hash
site = Nanoc::Int::SiteLoader.new.new_with_config(foo: 'bar')
assert_equal 'bar', site.config[:foo]
end
def test_initialize_with_incomplete_data_source_config
site = Nanoc::Int::SiteLoader.new.new_with_config(data_sources: [{ items_root: '/bar/' }])
assert_equal('filesystem', site.config[:data_sources][0][:type])
assert_equal('/bar/', site.config[:data_sources][0][:items_root])
assert_equal('/', site.config[:data_sources][0][:layouts_root])
assert_equal({}, site.config[:data_sources][0][:config])
end
def test_initialize_with_existing_parent_config_file
File.open('nanoc.yaml', 'w') do |io|
io.write <<-EOF
output_dir: public_html
parent_config_file: foo/foo.yaml
EOF
end
FileUtils.mkdir_p('foo')
FileUtils.cd('foo') do
File.open('foo.yaml', 'w') do |io|
io.write <<-EOF
parent_config_file: ../bar/bar.yaml
EOF
end
end
FileUtils.mkdir_p('bar')
FileUtils.cd('bar') do
File.open('bar.yaml', 'w') do |io|
io.write <<-EOF
enable_output_diff: true
foo: bar
output_dir: output
EOF
end
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
assert_nil site.config[:parent_config_file]
assert site.config[:enable_output_diff]
assert_equal 'bar', site.config[:foo]
assert_equal 'public_html', site.config[:output_dir]
end
def test_initialize_with_missing_parent_config_file
File.open('nanoc.yaml', 'w') do |io|
io.write <<-EOF
parent_config_file: foo/foo.yaml
EOF
end
assert_raises(Nanoc::Int::ConfigLoader::NoParentConfigFileFoundError) do
Nanoc::Int::SiteLoader.new.new_from_cwd
end
end
def test_initialize_with_parent_config_file_cycle
File.open('nanoc.yaml', 'w') do |io|
io.write <<-EOF
parent_config_file: foo/foo.yaml
EOF
end
FileUtils.mkdir_p('foo')
FileUtils.cd('foo') do
File.open('foo.yaml', 'w') do |io|
io.write <<-EOF
parent_config_file: ../nanoc.yaml
EOF
end
end
assert_raises(Nanoc::Int::ConfigLoader::CyclicalConfigFileError) do
Nanoc::Int::SiteLoader.new.new_from_cwd
end
end
def test_identifier_classes
Nanoc::CLI.run %w( create_site bar)
FileUtils.cd('bar') do
FileUtils.mkdir_p('content')
FileUtils.mkdir_p('layouts')
File.open('content/foo_bar.md', 'w') { |io| io << 'asdf' }
File.open('layouts/detail.erb', 'w') { |io| io << 'asdf' }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.items.each do |item|
assert_equal Nanoc::Identifier, item.identifier.class
end
site.layouts.each do |layout|
assert_equal Nanoc::Identifier, layout.identifier.class
end
end
end
def test_multiple_items_with_same_identifier
with_site do
File.open('content/sam.html', 'w') { |io| io.write('I am Sam!') }
FileUtils.mkdir_p('content/sam')
File.open('content/sam/index.html', 'w') { |io| io.write('I am Sam, too!') }
assert_raises(Nanoc::Int::Errors::DuplicateIdentifier) do
Nanoc::Int::SiteLoader.new.new_from_cwd
end
end
end
def test_multiple_layouts_with_same_identifier
with_site do
File.open('layouts/sam.html', 'w') { |io| io.write('I am Sam!') }
FileUtils.mkdir_p('layouts/sam')
File.open('layouts/sam/index.html', 'w') { |io| io.write('I am Sam, too!') }
assert_raises(Nanoc::Int::Errors::DuplicateIdentifier) do
Nanoc::Int::SiteLoader.new.new_from_cwd
end
end
end
end
nanoc-4.1.4/test/base/test_item.rb0000644000004100000410000000162312665031555017073 0ustar www-datawww-dataclass Nanoc::Int::ItemTest < Nanoc::TestCase
def test_initialize_with_attributes_with_string_keys
item = Nanoc::Int::Item.new('foo', { 'abc' => 'xyz' }, '/foo/')
assert_equal nil, item.attributes['abc']
assert_equal 'xyz', item.attributes[:abc]
end
def test_reference
item = Nanoc::Int::Item.new(
'content',
{ one: 'one in item' },
'/path/',
)
assert_equal([:item, '/path/'], item.reference)
end
def test_attributes
item = Nanoc::Int::Item.new('content', { 'one' => 'one in item' }, '/path/')
assert_equal({ one: 'one in item' }, item.attributes)
end
def test_freeze_should_disallow_changes
item = Nanoc::Int::Item.new('foo', { a: { b: 123 } }, '/foo/')
item.freeze
assert_raises_frozen_error do
item.attributes[:abc] = '123'
end
assert_raises_frozen_error do
item.attributes[:a][:b] = '456'
end
end
end
nanoc-4.1.4/test/base/temp_filename_factory_spec.rb0000644000004100000410000000327112665031555022445 0ustar www-datawww-datadescribe Nanoc::Int::TempFilenameFactory do
subject do
Nanoc::Int::TempFilenameFactory.new
end
let(:prefix) { 'foo' }
describe '#create' do
it 'should create unique paths' do
path_a = subject.create(prefix)
path_b = subject.create(prefix)
path_a.wont_equal(path_b)
end
it 'should return absolute paths' do
path = subject.create(prefix)
path.must_match(/\A\//)
end
it 'should create the containing directory' do
Dir[subject.root_dir + '/**/*'].must_equal([])
path = subject.create(prefix)
File.directory?(File.dirname(path)).must_equal(true)
end
it 'should reuse the same path after cleanup' do
path_a = subject.create(prefix)
subject.cleanup(prefix)
path_b = subject.create(prefix)
path_a.must_equal(path_b)
end
end
describe '#cleanup' do
it 'should remove generated files' do
path_a = subject.create(prefix)
File.file?(path_a).wont_equal(true) # not yet used
File.open(path_a, 'w') { |io| io << 'hi!' }
File.file?(path_a).must_equal(true)
subject.cleanup(prefix)
File.file?(path_a).wont_equal(true)
end
it 'should eventually delete the root directory' do
subject.create(prefix)
File.directory?(subject.root_dir).must_equal(true)
subject.cleanup(prefix)
File.directory?(subject.root_dir).wont_equal(true)
end
end
describe 'other instance' do
let(:other_instance) do
Nanoc::Int::TempFilenameFactory.new
end
it 'should create unique paths across instances' do
path_a = subject.create(prefix)
path_b = other_instance.create(prefix)
path_a.wont_equal(path_b)
end
end
end
nanoc-4.1.4/test/base/test_checksum_store.rb0000644000004100000410000000126412665031555021154 0ustar www-datawww-dataclass Nanoc::Int::ChecksumStoreTest < Nanoc::TestCase
def test_get_with_existing_object
require 'pstore'
# Create store
FileUtils.mkdir_p('tmp')
pstore = PStore.new('tmp/checksums')
pstore.transaction do
pstore[:data] = { [:item, '/moo/'] => 'zomg' }
pstore[:version] = 1
end
# Check
store = Nanoc::Int::ChecksumStore.new
store.load
obj = Nanoc::Int::Item.new('Moo?', {}, '/moo/')
assert_equal 'zomg', store[obj]
end
def test_get_with_nonexistant_object
store = Nanoc::Int::ChecksumStore.new
store.load
# Check
obj = Nanoc::Int::Item.new('Moo?', {}, '/animals/cow/')
assert_equal nil, store[obj]
end
end
nanoc-4.1.4/test/base/test_compiler.rb0000644000004100000410000003370012665031555017750 0ustar www-datawww-dataclass Nanoc::Int::CompilerTest < Nanoc::TestCase
def new_compiler(site = nil)
site ||= Nanoc::Int::Site.new(
config: nil,
code_snippets: [],
items: [],
layouts: [],
)
reps = Nanoc::Int::ItemRepRepo.new
action_provider = Nanoc::Int::ActionProvider.named(:rule_dsl).for(site)
params = {
compiled_content_cache: Nanoc::Int::CompiledContentCache.new,
checksum_store: Nanoc::Int::ChecksumStore.new(site: site),
rule_memory_store: Nanoc::Int::RuleMemoryStore.new,
dependency_store: Nanoc::Int::DependencyStore.new(
site.items.to_a + site.layouts.to_a),
action_provider: action_provider,
reps: reps,
}
params[:outdatedness_checker] =
Nanoc::Int::OutdatednessChecker.new(
site: site,
checksum_store: params[:checksum_store],
dependency_store: params[:dependency_store],
rule_memory_store: params[:rule_memory_store],
action_provider: action_provider,
reps: reps,
)
Nanoc::Int::Compiler.new(site, params)
end
def test_compile_rep_should_write_proper_snapshots_real
with_site do |site|
File.write('content/moo.txt', '<%= 1 %> <%%= 2 %> <%%%= 3 %>')
File.write('layouts/default.erb', 'head <%= yield %> foot')
File.open('Rules', 'w') do |io|
io.write "compile '/**/*' do\n"
io.write " filter :erb\n"
io.write " filter :erb\n"
io.write " layout 'default'\n"
io.write " filter :erb\n"
io.write "end\n"
io.write "\n"
io.write "route '/**/*', snapshot: :raw do\n"
io.write " '/moo-raw.txt'\n"
io.write "end\n"
io.write "\n"
io.write "route '/**/*', snapshot: :pre do\n"
io.write " '/moo-pre.txt'\n"
io.write "end\n"
io.write "\n"
io.write "route '/**/*', snapshot: :post do\n"
io.write " '/moo-post.txt'\n"
io.write "end\n"
io.write "\n"
io.write "route '/**/*' do\n"
io.write " '/moo-last.txt'\n"
io.write "end\n"
io.write "\n"
io.write "layout '/**/*', :erb\n"
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
assert File.file?('output/moo-raw.txt')
# assert File.file?('output/moo-pre.txt')
assert File.file?('output/moo-post.txt')
assert File.file?('output/moo-last.txt')
assert_equal '<%= 1 %> <%%= 2 %> <%%%= 3 %>', File.read('output/moo-raw.txt')
# assert_equal '1 2 <%= 3 %>', File.read('output/moo-pre.txt')
assert_equal 'head 1 2 3 foot', File.read('output/moo-post.txt')
assert_equal 'head 1 2 3 foot', File.read('output/moo-last.txt')
end
end
def test_compile_with_no_reps
with_site do |site|
site.compile
assert Dir['output/*'].empty?
end
end
def test_compile_with_one_rep
with_site do |site|
File.open('content/index.html', 'w') { |io| io.write('o hello') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
assert Dir['output/*'].size == 1
assert File.file?('output/index.html')
assert File.read('output/index.html') == 'o hello'
end
end
def test_compile_with_two_independent_reps
with_site do |site|
File.open('content/foo.html', 'w') { |io| io.write('o hai') }
File.open('content/bar.html', 'w') { |io| io.write('o bai') }
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
assert Dir['output/*'].size == 2
assert File.file?('output/foo/index.html')
assert File.file?('output/bar/index.html')
assert File.read('output/foo/index.html') == 'o hai'
assert File.read('output/bar/index.html') == 'o bai'
end
end
def test_compile_with_two_dependent_reps
with_site(compilation_rule_content: 'filter :erb') do |site|
File.open('content/foo.html', 'w') do |io|
io.write('<%= @items.find { |i| i.identifier == "/bar/" }.compiled_content %>!!!')
end
File.open('content/bar.html', 'w') do |io|
io.write('manatee')
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
assert Dir['output/*'].size == 2
assert File.file?('output/foo/index.html')
assert File.file?('output/bar/index.html')
assert File.read('output/foo/index.html') == 'manatee!!!'
assert File.read('output/bar/index.html') == 'manatee'
end
end
def test_compile_with_two_mutually_dependent_reps
with_site(compilation_rule_content: 'filter :erb') do |site|
File.open('content/foo.html', 'w') do |io|
io.write('<%= @items.find { |i| i.identifier == "/bar/" }.compiled_content %>')
end
File.open('content/bar.html', 'w') do |io|
io.write('<%= @items.find { |i| i.identifier == "/foo/" }.compiled_content %>')
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
assert_raises Nanoc::Int::Errors::RecursiveCompilation do
site.compile
end
end
end
def test_disallow_routes_not_starting_with_slash
# Create site
Nanoc::CLI.run %w( create_site bar)
FileUtils.cd('bar') do
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '/**/*' do\n"
io.write " layout 'default'\n"
io.write "end\n"
io.write "\n"
io.write "route '/**/*' do\n"
io.write " 'index.html'\n"
io.write "end\n"
io.write "\n"
io.write "layout '/**/*', :erb\n"
end
# Create site
site = Nanoc::Int::SiteLoader.new.new_from_cwd
error = assert_raises(Nanoc::Error) do
site.compile
end
assert_match(/^The path returned for the.*does not start with a slash. Please ensure that all routing rules return a path that starts with a slash./, error.message)
end
end
def test_disallow_duplicate_routes
# Create site
Nanoc::CLI.run %w( create_site bar)
FileUtils.cd('bar') do
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '/**/*' do\n"
io.write "end\n"
io.write "\n"
io.write "route '/**/*' do\n"
io.write " '/index.html'\n"
io.write "end\n"
end
# Create files
File.write('content/foo.html', 'asdf')
File.write('content/bar.html', 'asdf')
# Create site
site = Nanoc::Int::SiteLoader.new.new_from_cwd
assert_raises(Nanoc::Int::ItemRepRouter::IdenticalRoutesError) do
site.compile
end
end
end
def test_compile_should_recompile_all_reps
Nanoc::CLI.run %w( create_site bar )
FileUtils.cd('bar') do
Nanoc::CLI.run %w( compile )
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# At this point, even the already compiled items in the previous pass
# should have their compiled content assigned, so this should work:
site.compiler.reps[site.items['/index.*']][0].compiled_content
end
end
def test_disallow_multiple_snapshots_with_the_same_name
# Create site
Nanoc::CLI.run %w( create_site bar )
FileUtils.cd('bar') do
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '/**/*' do\n"
io.write " snapshot :aaa\n"
io.write " snapshot :aaa\n"
io.write "end\n"
io.write "\n"
io.write "route '/**/*' do\n"
io.write " item.identifier.to_s\n"
io.write "end\n"
io.write "\n"
io.write "layout '/**/*', :erb\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
assert_raises Nanoc::Int::Errors::CannotCreateMultipleSnapshotsWithSameName do
site.compile
end
end
end
def test_include_compiled_content_of_active_item_at_previous_snapshot
with_site do |site|
# Create item
File.open('content/index.html', 'w') do |io|
io.write('[<%= @item.compiled_content(:snapshot => :aaa) %>]')
end
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write " snapshot :aaa\n"
io.write " filter :erb\n"
io.write " filter :erb\n"
io.write "end\n"
io.write "\n"
io.write "route '*' do\n"
io.write " '/index.html'\n"
io.write "end\n"
io.write "\n"
io.write "layout '*', :erb\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert_equal '[[[<%= @item.compiled_content(:snapshot => :aaa) %>]]]', File.read('output/index.html')
end
end
def test_mutually_include_compiled_content_at_previous_snapshot
with_site do |site|
# Create items
File.open('content/a.html', 'w') do |io|
io.write('[<%= @items.find { |i| i.identifier == "/z/" }.compiled_content(:snapshot => :guts) %>]')
end
File.open('content/z.html', 'w') do |io|
io.write('stuff')
end
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write " snapshot :guts\n"
io.write " filter :erb\n"
io.write "end\n"
io.write "\n"
io.write "route '*' do\n"
io.write " item.identifier + 'index.html'\n"
io.write "end\n"
io.write "\n"
io.write "layout '*', :erb\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert_equal '[stuff]', File.read('output/a/index.html')
assert_equal 'stuff', File.read('output/z/index.html')
end
end
def test_layout_with_extra_filter_args
with_site do |site|
# Create item
File.open('content/index.html', 'w') do |io|
io.write('This is <%= @foo %>.')
end
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write " filter :erb, :locals => { :foo => 123 }\n"
io.write "end\n"
io.write "\n"
io.write "route '*' do\n"
io.write " '/index.html'\n"
io.write "end\n"
io.write "\n"
io.write "layout '*', :erb\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert_equal 'This is 123.', File.read('output/index.html')
end
end
def test_change_routing_rule_and_recompile
with_site do |site|
# Create items
File.open('content/a.html', 'w') do |io|
io.write('A
')
end
File.open('content/b.html', 'w') do |io|
io.write('B
')
end
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write "end\n"
io.write "\n"
io.write "route '/a/' do\n"
io.write " '/index.html'\n"
io.write "end\n"
io.write "\n"
io.write "route '*' do\n"
io.write " nil\n"
io.write "end\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert_equal 'A
', File.read('output/index.html')
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write "end\n"
io.write "\n"
io.write "route '/b/' do\n"
io.write " '/index.html'\n"
io.write "end\n"
io.write "\n"
io.write "route '*' do\n"
io.write " nil\n"
io.write "end\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert_equal 'B
', File.read('output/index.html')
end
end
def test_rep_assigns
with_site do |site|
# Create item
File.open('content/index.html', 'w') do |io|
io.write('@rep.name = <%= @rep.name %> - @item_rep.name = <%= @item_rep.name %>')
end
# Create routes
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write " if @rep.name == :default && @item_rep.name == :default\n"
io.write " filter :erb\n"
io.write " end\n"
io.write "end\n"
io.write "\n"
io.write "route '*' do\n"
io.write " '/index.html'\n"
io.write "end\n"
io.write "\n"
io.write "layout '*', :erb\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert_equal '@rep.name = default - @item_rep.name = default', File.read('output/index.html')
end
end
def test_unfiltered_binary_item_should_not_be_moved_outside_content
with_site do
File.open('content/blah.dat', 'w') { |io| io.write('o hello') }
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write "end\n"
io.write "\n"
io.write "route '*' do\n"
io.write " item.identifier.chop + '.' + item[:extension]\n"
io.write "end\n"
io.write "\n"
io.write "layout '*', :erb\n"
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
assert_equal Set.new(%w( content/blah.dat )), Set.new(Dir['content/*'])
assert_equal Set.new(%w( output/blah.dat )), Set.new(Dir['output/*'])
end
end
def test_tmp_text_items_are_removed_after_compilation
with_site do |site|
# Create item
File.open('content/index.html', 'w') do |io|
io.write('stuff')
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert Dir['tmp/text_items/*'].empty?
end
end
def test_find_layouts_by_glob
Nanoc::CLI.run %w( create_site bar )
FileUtils.cd('bar') do
File.open('Rules', 'w') do |io|
io.write "compile '/**/*' do\n"
io.write " layout '/default.*'\n"
io.write "end\n"
io.write "\n"
io.write "route '/**/*' do\n"
io.write " item.identifier.to_s\n"
io.write "end\n"
io.write "\n"
io.write "layout '/**/*', :erb\n"
end
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
end
end
end
nanoc-4.1.4/test/base/test_context.rb0000644000004100000410000000123312665031555017616 0ustar www-datawww-dataclass Nanoc::Int::ContextTest < Nanoc::TestCase
def test_context_with_instance_variable
# Create context
context = Nanoc::Int::Context.new({ foo: 'bar', baz: 'quux' })
# Ensure correct evaluation
assert_equal('bar', eval('@foo', context.get_binding))
end
def test_context_with_instance_method
# Create context
context = Nanoc::Int::Context.new({ foo: 'bar', baz: 'quux' })
# Ensure correct evaluation
assert_equal('bar', eval('foo', context.get_binding))
end
def test_example
# Parse
YARD.parse(LIB_DIR + '/nanoc/base/context.rb')
# Run
assert_examples_correct 'Nanoc::Int::Context#initialize'
end
end
nanoc-4.1.4/test/base/test_filter.rb0000644000004100000410000000330212665031555017416 0ustar www-datawww-dataclass Nanoc::FilterTest < Nanoc::TestCase
def test_initialize
# Create filter
filter = Nanoc::Filter.new
# Test assigns
assert_equal({}, filter.instance_eval { @assigns })
end
def test_assigns
# Create filter
filter = Nanoc::Filter.new({ foo: 'bar' })
# Check assigns
assert_equal('bar', filter.assigns[:foo])
end
def test_assigns_with_instance_variables
# Create filter
filter = Nanoc::Filter.new({ foo: 'bar' })
# Check assigns
assert_equal('bar', filter.instance_eval { @foo })
end
def test_assigns_with_instance_methods
# Create filter
filter = Nanoc::Filter.new({ foo: 'bar' })
# Check assigns
assert_equal('bar', filter.instance_eval { foo })
end
def test_run
# Create filter
filter = Nanoc::Filter.new
# Make sure an error is raised
assert_raises(NotImplementedError) do
filter.run(nil)
end
end
def test_filename_item
# Mock items
item = mock
item.expects(:identifier).returns('/foo/bar/baz/')
item_rep = mock
item_rep.expects(:name).returns(:quux)
# Create filter
filter = Nanoc::Filter.new({ item: item, item_rep: item_rep })
# Check filename
assert_equal('item /foo/bar/baz/ (rep quux)', filter.filename)
end
def test_filename_layout
# Mock items
layout = mock
layout.expects(:identifier).returns('/wohba/')
# Create filter
filter = Nanoc::Filter.new({ item: mock, item_rep: mock, layout: layout })
# Check filename
assert_equal('layout /wohba/', filter.filename)
end
def test_filename_unknown
# Create filter
filter = Nanoc::Filter.new({})
# Check filename
assert_equal('?', filter.filename)
end
end
nanoc-4.1.4/test/base/test_code_snippet.rb0000644000004100000410000000116712665031555020614 0ustar www-datawww-dataclass Nanoc::Int::CodeSnippetTest < Nanoc::TestCase
def test_load
# Initialize
$complete_insane_parrot = 'meow'
# Create code and load it
code_snippet = Nanoc::Int::CodeSnippet.new("$complete_insane_parrot = 'woof'", 'parrot.rb')
code_snippet.load
# Ensure code is loaded
assert_equal('woof', $complete_insane_parrot)
end
def test_load_with_toplevel_binding
# Initialize
@foo = 'meow'
# Create code and load it
code_snippet = Nanoc::Int::CodeSnippet.new("@foo = 'woof'", 'dog.rb')
code_snippet.load
# Ensure binding is correct
assert_equal('meow', @foo)
end
end
nanoc-4.1.4/test/base/test_data_source.rb0000644000004100000410000000312712665031555020427 0ustar www-datawww-dataclass Nanoc::DataSourceTest < Nanoc::TestCase
def test_loading
# Create data source
data_source = Nanoc::DataSource.new(nil, nil, nil, nil)
data_source.expects(:up).times(1)
data_source.expects(:down).times(1)
# Test nested loading
assert_equal(0, data_source.instance_eval { @references })
data_source.loading do
assert_equal(1, data_source.instance_eval { @references })
data_source.loading do
assert_equal(2, data_source.instance_eval { @references })
end
assert_equal(1, data_source.instance_eval { @references })
end
assert_equal(0, data_source.instance_eval { @references })
end
def test_not_implemented
# Create data source
data_source = Nanoc::DataSource.new(nil, nil, nil, nil)
# Test optional methods
data_source.up
data_source.down
# Test methods - loading data
assert_equal [], data_source.items
assert_equal [], data_source.layouts
end
def test_new_item
data_source = Nanoc::DataSource.new(nil, nil, nil, nil)
item = data_source.new_item('stuff', { title: 'Stuff!' }, '/asdf/')
assert_equal 'stuff', item.content.string
assert_equal 'Stuff!', item.attributes[:title]
assert_equal Nanoc::Identifier.new('/asdf/'), item.identifier
end
def test_new_layout
data_source = Nanoc::DataSource.new(nil, nil, nil, nil)
layout = data_source.new_layout('stuff', { title: 'Stuff!' }, '/asdf/')
assert_equal 'stuff', layout.content.string
assert_equal 'Stuff!', layout.attributes[:title]
assert_equal Nanoc::Identifier.new('/asdf/'), layout.identifier
end
end
nanoc-4.1.4/test/base/test_store.rb0000644000004100000410000000126412665031555017272 0ustar www-datawww-dataclass Nanoc::Int::StoreTest < Nanoc::TestCase
class TestStore < Nanoc::Int::Store
def data
@data
end
def data=(new_data)
@data = new_data
end
end
def test_delete_and_reload_on_error
store = TestStore.new('test.db', 1)
# Create
store.load
store.data = { fun: 'sure' }
store.store
# Test stored values
store = TestStore.new('test.db', 1)
store.load
assert_equal({ fun: 'sure' }, store.data)
# Mess up
File.open('test.db', 'w') do |io|
io << 'Damn {}#}%@}$^)@&$&*^#@ broken stores!!!'
end
# Reload
store = TestStore.new('test.db', 1)
store.load
assert_equal(nil, store.data)
end
end
nanoc-4.1.4/test/helper.rb0000644000004100000410000001460612665031555015450 0ustar www-datawww-datarequire 'simplecov'
SimpleCov.start
require 'minitest/test'
require 'minitest/spec'
require 'minitest/mock'
require 'minitest/autorun'
require 'mocha/setup'
require 'vcr'
require 'tmpdir'
require 'stringio'
require 'yard'
VCR.configure do |c|
c.cassette_library_dir = 'test/fixtures/vcr_cassettes'
c.hook_into :webmock
end
require 'nanoc'
require 'nanoc/cli'
Nanoc::CLI.setup
module Nanoc::TestHelpers
LIB_DIR = File.expand_path(File.dirname(__FILE__) + '/../lib')
def disable_nokogiri?
ENV.key?('DISABLE_NOKOGIRI')
end
def if_have(*libs)
libs.each do |lib|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' && lib == 'nokogiri' && disable_nokogiri?
skip 'Pure Java Nokogiri has issues that cause problems with nanoc (see https://github.com/nanoc/nanoc/pull/422) -- run without DISABLE_NOKOGIRI to enable Nokogiri tests'
end
begin
require lib
rescue LoadError
skip "requiring #{lib} failed"
end
end
yield
end
def if_implemented
yield
rescue NotImplementedError, NameError
skip $ERROR_INFO
return
end
def with_site(params = {})
# Build site name
site_name = params[:name]
if site_name.nil?
@site_num ||= 0
site_name = "site-#{@site_num}"
@site_num += 1
end
# Build rules
rules_content = < ...')
end
end
File.open('nanoc.yaml', 'w') do |io|
io << 'string_pattern_type: legacy' << "\n" if params.fetch(:legacy, true)
io << 'data_sources:' << "\n"
io << ' -' << "\n"
io << ' type: filesystem' << "\n"
io << ' identifier_type: legacy' << "\n" if params.fetch(:legacy, true)
end
File.open('Rules', 'w') { |io| io.write(rules_content) }
end
end
# Yield site
FileUtils.cd(site_name) do
yield Nanoc::Int::SiteLoader.new.new_from_cwd
end
end
def setup
# Check skipped
if ENV['skip']
if ENV['skip'].split(',').include?(self.class.to_s)
skip 'manually skipped'
end
end
# Clean up
GC.start
# Go quiet
unless ENV['QUIET'] == 'false'
@orig_stdout = $stdout
@orig_stderr = $stderr
$stdout = StringIO.new
$stderr = StringIO.new
end
# Enter tmp
@tmp_dir = Dir.mktmpdir('nanoc-test')
@orig_wd = FileUtils.pwd
FileUtils.cd(@tmp_dir)
# Let us get to the raw errors
Nanoc::CLI::ErrorHandler.disable
end
def teardown
# Restore normal error handling
Nanoc::CLI::ErrorHandler.enable
# Exit tmp
FileUtils.cd(@orig_wd)
FileUtils.rm_rf(@tmp_dir)
# Go unquiet
unless ENV['QUIET'] == 'false'
$stdout = @orig_stdout
$stderr = @orig_stderr
end
end
def capturing_stdio(&_block)
# Store
orig_stdout = $stdout
orig_stderr = $stderr
# Run
$stdout = StringIO.new
$stderr = StringIO.new
yield
{ stdout: $stdout.string, stderr: $stderr.string }
ensure
# Restore
$stdout = orig_stdout
$stderr = orig_stderr
end
# Adapted from http://github.com/lsegal/yard-examples/tree/master/doctest
def assert_examples_correct(object)
P(object).tags(:example).each do |example|
# Classify
lines = example.text.lines.map do |line|
[line =~ /^\s*# ?=>/ ? :result : :code, line]
end
# Join
pieces = []
lines.each do |line|
if !pieces.empty? && pieces.last.first == line.first
pieces.last.last << line.last
else
pieces << line
end
end
lines = pieces.map(&:last)
# Test
b = binding
lines.each_slice(2) do |pair|
actual_out = eval(pair.first, b)
expected_out = eval(pair.last.match(/# ?=>(.*)/)[1], b)
assert_equal(
expected_out,
actual_out,
"Incorrect example:\n#{pair.first}",
)
end
end
end
def assert_contains_exactly(expected, actual)
assert_equal(
expected.size,
actual.size,
format('Expected %s to be of same size as %s', actual.inspect, expected.inspect),
)
remaining = actual.dup.to_a
expected.each do |e|
index = remaining.index(e)
remaining.delete_at(index) if index
end
assert(
remaining.empty?,
format('Expected %s to contain all the elements of %s', actual.inspect, expected.inspect),
)
end
def assert_raises_frozen_error
error = assert_raises(RuntimeError, TypeError) { yield }
assert_match(/(^can't modify frozen |^unable to modify frozen object$)/, error.message)
end
def with_env_vars(hash, &_block)
orig_env_hash = ENV.to_hash
hash.each_pair { |k, v| ENV[k] = v }
yield
ensure
orig_env_hash.each_pair { |k, v| ENV[k] = v }
end
def on_windows?
Nanoc.on_windows?
end
def command?(cmd)
which, null = on_windows? ? %w(where NUL) : ['which', '/dev/null']
system("#{which} #{cmd} > #{null} 2>&1")
end
def symlinks_supported?
File.symlink nil, nil
rescue NotImplementedError
return false
rescue
return true
end
def skip_unless_have_command(cmd)
skip "Could not find external command \"#{cmd}\"" unless command?(cmd)
end
def skip_unless_symlinks_supported
skip 'Symlinks are not supported by Ruby on Windows' unless symlinks_supported?
end
def root_dir
File.absolute_path(File.dirname(__FILE__) + '/..')
end
end
class Nanoc::TestCase < Minitest::Test
include Nanoc::TestHelpers
end
# Unexpected system exit is unexpected
::Minitest::Test::PASSTHROUGH_EXCEPTIONS.delete(SystemExit)
# A more precise inspect method for Time improves assert failure messages.
#
class Time
def inspect
strftime("%a %b %d %H:%M:%S.#{format('%06d', usec)} %Z %Y")
end
end
nanoc-4.1.4/test/filters/0000755000004100000410000000000012665031555015305 5ustar www-datawww-datananoc-4.1.4/test/filters/test_colorize_syntax.rb0000644000004100000410000003200612665031555022126 0ustar www-datawww-dataclass Nanoc::Filters::ColorizeSyntaxTest < Nanoc::TestCase
CODERAY_PRE = ''.freeze
CODERAY_POST = ''.freeze
def test_coderay_simple
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = '# comment
'
expected_output = CODERAY_PRE + '# comment
' + CODERAY_POST
# Run filter
actual_output = filter.setup_and_run(input)
assert_equal(expected_output, actual_output)
end
end
def test_dummy
if_have 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = '# comment
'
expected_output = input # because we are using a dummy
# Run filter
actual_output = filter.setup_and_run(input, default_colorizer: :dummy)
assert_equal(expected_output, actual_output)
end
end
def test_with_frozen_input
if_have 'nokogiri' do
input = '# comment
'.freeze
input.freeze
filter = ::Nanoc::Filters::ColorizeSyntax.new
filter.setup_and_run(input, default_colorizer: :dummy)
end
end
def test_full_page
if_have 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = <
Foo
# comment
EOS
expected_output_regex = %r{^\s*\s*\s*\s*Foo \s*\s*\s*# comment
\s*\s*}
# Run filter
actual_output = filter.setup_and_run(input, default_colorizer: :dummy, is_fullpage: true)
assert_match expected_output_regex, actual_output
end
end
def test_coderay_with_comment
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = %(#!ruby
# comment
)
expected_output = CODERAY_PRE + '# comment
' + CODERAY_POST
# Run filter
actual_output = filter.setup_and_run(input)
assert_equal(expected_output, actual_output)
end
end
def test_coderay_with_comment_in_middle
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = %(def moo ; end
#!ruby
# comment
)
expected_output = "def moo ; end\n#!ruby\n# comment
"
# Run filter
actual_output = filter.setup_and_run(input)
assert_equal(expected_output, actual_output)
end
end
def test_coderay_with_comment_and_class
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = %(#!ruby
# comment
)
expected_output = CODERAY_PRE + %(#!ruby
# comment
) + CODERAY_POST
# Run filter
actual_output = filter.setup_and_run(input)
assert_equal(expected_output, actual_output)
end
end
def test_coderay_with_more_classes
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = '# comment
'
expected_output = CODERAY_PRE + '# comment
' + CODERAY_POST
# Run filter
actual_output = filter.setup_and_run(input)
assert_equal(expected_output, actual_output)
end
end
def test_pygmentize
if_have 'nokogiri' do
skip_unless_have_command 'pygmentize'
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = '# comment
'
expected_output = '# comment
'
# Run filter
actual_output = filter.setup_and_run(input, colorizers: { ruby: :pygmentize })
assert_equal(expected_output, actual_output)
end
end
def test_pygmentsrb
skip 'pygments.rb does not support Windows' if on_windows?
if_have 'pygments', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = '# comment…
'
expected_output = '# comment…
'
# Run filter
actual_output = filter.setup_and_run(input, colorizers: { ruby: :pygmentsrb })
assert_equal(expected_output, actual_output)
end
end
def test_simon_highlight
if_have 'nokogiri' do
skip_unless_have_command 'highlight'
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = %(
# comment
)
expected_output = '# comment
'
# Run filter
actual_output = filter.setup_and_run(input, default_colorizer: :simon_highlight)
assert_equal(expected_output, actual_output)
end
end
def test_colorize_syntax_with_unknown_syntax
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Run filter
assert_raises RuntimeError do
filter.setup_and_run('whatever
', syntax: :kasflwafhaweoineurl)
end
end
end
def test_colorize_syntax_with_xml
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = 'foo
bar
'
expected_output = 'foo
bar
'
# Run filter
actual_output = filter.setup_and_run(input, syntax: :xml)
assert_equal(expected_output, actual_output)
end
end
def test_colorize_syntax_with_xhtml
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = 'foo
bar
'
expected_output = 'foo
bar
'
# Run filter
actual_output = filter.setup_and_run(input, syntax: :xhtml)
assert_equal(expected_output, actual_output)
end
end
def test_colorize_syntax_with_default_colorizer
skip_unless_have_command 'pygmentize'
if_have 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = 'puts "foo"
'
expected_output = 'puts "foo"
'
# Run filter
actual_output = filter.setup_and_run(input, default_colorizer: :pygmentize)
assert_equal(expected_output, actual_output)
end
end
def test_colorize_syntax_with_missing_executables
if_have 'nokogiri' do
begin
original_path = ENV['PATH']
ENV['PATH'] = './blooblooblah'
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = 'puts "foo"
'
# Run filter
[:albino, :pygmentize, :simon_highlight].each do |colorizer|
begin
input = 'puts "foo"
'
filter.setup_and_run(
input,
colorizers: { ruby: colorizer })
flunk 'expected colorizer to raise if no executable is available'
rescue
end
end
ensure
ENV['PATH'] = original_path
end
end
end
def test_colorize_syntax_with_non_language_shebang_line
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = <
#!/usr/bin/env ruby
puts 'hi!'
after
EOS
expected_output = <
#!/usr/bin/env ruby
puts 'hi!'
after
EOS
# Run filter
actual_output = filter.setup_and_run(input).sub(/\s*\Z/m, '')
assert_equal(expected_output, actual_output)
end
end
def test_colorize_syntax_with_non_language_shebang_line_and_language_line
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = <
#!ruby
#!/usr/bin/env ruby
puts 'hi!'
after
EOS
expected_output = <#!/usr/bin/env ruby
puts 'hi!'
#{CODERAY_POST}
after
EOS
# Run filter
actual_output = filter.setup_and_run(input).sub(/\s*\Z/m, '')
assert_equal(expected_output, actual_output)
end
end
def test_not_outside_pre
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = '# comment
'
expected_output = '# comment
'
# Run filter
actual_output = filter.setup_and_run(input, outside_pre: false)
assert_equal(expected_output, actual_output)
end
end
def test_outside_pre
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = '# comment
'
expected_output = '# comment
'
# Run filter
actual_output = filter.setup_and_run(input, outside_pre: true)
assert_equal(expected_output, actual_output)
end
end
def test_strip
if_have 'coderay', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Simple test
assert_equal ' bar', filter.send(:strip, "\n bar")
# Get input and expected output
input = <
def foo
end
after
EOS
expected_output = < def foo
end
#{CODERAY_POST}
after
EOS
# Run filter
actual_output = filter.setup_and_run(input).sub(/\s*\Z/m, '')
assert_equal(expected_output, actual_output)
end
end
def test_rouge
if_have 'rouge', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = <
def foo
end
after
EOS
expected_output = < def foo
end
after
EOS
# Run filter
actual_output = filter.setup_and_run(input, default_colorizer: :rouge)
assert_equal(expected_output, actual_output)
end
end
def test_rouge_with_css_class
if_have 'rouge', 'nokogiri' do
# Create filter
filter = ::Nanoc::Filters::ColorizeSyntax.new
# Get input and expected output
input = <
def foo
end
after
EOS
expected_output = < def foo
end
after
EOS
# Run filter
actual_output = filter.setup_and_run(input, default_colorizer: :rouge, rouge: { css_class: 'my-class' })
assert_equal(expected_output, actual_output)
end
end
end
nanoc-4.1.4/test/filters/test_slim.rb0000644000004100000410000000162612665031555017642 0ustar www-datawww-dataclass Nanoc::Filters::SlimTest < Nanoc::TestCase
def test_filter
if_have 'slim' do
# Create filter
filter = ::Nanoc::Filters::Slim.new({ rabbit: 'The rabbit is on the branch.' })
# Run filter (no assigns)
result = filter.setup_and_run('html')
assert_match(/.*<\/html>/, result)
# Run filter (assigns without @)
result = filter.setup_and_run('p = rabbit')
assert_equal('The rabbit is on the branch.
', result)
# Run filter (assigns with @)
result = filter.setup_and_run('p = @rabbit')
assert_equal('The rabbit is on the branch.
', result)
end
end
def test_filter_with_yield
if_have 'slim' do
filter = ::Nanoc::Filters::Slim.new({ content: 'The rabbit is on the branch.' })
result = filter.setup_and_run('p = yield')
assert_equal('The rabbit is on the branch.
', result)
end
end
end
nanoc-4.1.4/test/filters/test_redcloth.rb0000644000004100000410000000143612665031555020501 0ustar www-datawww-dataclass Nanoc::Filters::RedClothTest < Nanoc::TestCase
def test_filter
if_have 'redcloth' do
# Get filter
filter = ::Nanoc::Filters::RedCloth.new
# Run filter
result = filter.setup_and_run('h1. Foo')
assert_equal('Foo
', result)
end
end
def test_filter_with_options
if_have 'redcloth' do
# Get filter
filter = ::Nanoc::Filters::RedCloth.new
# Run filter without options
result = filter.setup_and_run('I am a member of SPECTRE.')
assert_equal('I am a member of SPECTRE.
', result)
# Run filter with options
result = filter.setup_and_run('I am a member of SPECTRE.', no_span_caps: true)
assert_equal('I am a member of SPECTRE.
', result)
end
end
end
nanoc-4.1.4/test/filters/test_uglify_js.rb0000644000004100000410000000150512665031555020665 0ustar www-datawww-dataclass Nanoc::Filters::UglifyJSTest < Nanoc::TestCase
def test_filter
if_have 'uglifier' do
# Create filter
filter = ::Nanoc::Filters::UglifyJS.new
# Run filter
input = 'foo = 1; (function(bar) { if (true) alert(bar); })(foo)'
result = filter.setup_and_run(input)
assert_match(/foo=1,function\((.)\)\{alert\(\1\)\}\(foo\);/, result)
end
end
def test_filter_with_options
if_have 'uglifier' do
filter = ::Nanoc::Filters::UglifyJS.new
input = "if(donkey) alert('It is a donkey!');"
result = filter.setup_and_run(input, output: { beautify: false })
assert_equal 'donkey&&alert("It is a donkey!");', result
result = filter.setup_and_run(input, output: { beautify: true })
assert_equal 'donkey && alert("It is a donkey!");', result
end
end
end
nanoc-4.1.4/test/filters/test_yui_compressor.rb0000644000004100000410000000210112665031555021745 0ustar www-datawww-dataclass Nanoc::Filters::YUICompressorTest < Nanoc::TestCase
def test_filter_javascript
if_have 'yuicompressor' do
filter = ::Nanoc::Filters::YUICompressor.new
sample_js = <<-JAVASCRIPT
function factorial(n) {
var result = 1;
for (var i = 2; i <= n; i++) {
result *= i
}
return result;
}
JAVASCRIPT
result = filter.setup_and_run(sample_js, { type: 'js', munge: true })
assert_match 'function factorial(c){var a=1;for(var b=2;b<=c;b++){a*=b}return a};', result
result = filter.setup_and_run(sample_js, { type: 'js', munge: false })
assert_match 'function factorial(n){var result=1;for(var i=2;i<=n;i++){result*=i}return result};', result
end
end
def test_filter_css
if_have 'yuicompressor' do
filter = ::Nanoc::Filters::YUICompressor.new
sample_css = <<-CSS
* {
margin: 0;
}
CSS
result = filter.setup_and_run(sample_css, { type: 'css' })
assert_match '*{margin:0}', result
end
end
end
nanoc-4.1.4/test/filters/test_typogruby.rb0000644000004100000410000000102312665031555020731 0ustar www-datawww-dataclass Nanoc::Filters::TypogrubyTest < Nanoc::TestCase
def test_filter
if_have 'typogruby' do
# Get filter
filter = ::Nanoc::Filters::Typogruby.new
# Run filter
a = '"Typogruby makes HTML look smarter & better, don\'t you think?"'
b = '“Typogruby makes HTML look smarter & better, don’t you think?”'
result = filter.setup_and_run(a)
assert_equal(b, result)
end
end
end
nanoc-4.1.4/test/filters/test_maruku.rb0000644000004100000410000000051712665031555020200 0ustar www-datawww-dataclass Nanoc::Filters::MarukuTest < Nanoc::TestCase
def test_filter
if_have 'maruku' do
# Create filter
filter = ::Nanoc::Filters::Maruku.new
# Run filter
result = filter.setup_and_run('This is _so_ *cool*!')
assert_equal('This is so cool!
', result.strip)
end
end
end
nanoc-4.1.4/test/filters/test_coffeescript.rb0000644000004100000410000000055512665031555021352 0ustar www-datawww-dataclass Nanoc::Filters::CoffeeScriptTest < Nanoc::TestCase
def test_filter
if_have 'coffee-script' do
# Create filter
filter = ::Nanoc::Filters::CoffeeScript.new
# Run filter (no assigns)
result = filter.setup_and_run('alert 42')
assert_equal('(function() { alert(42); }).call(this); ', result.gsub(/\s+/, ' '))
end
end
end
nanoc-4.1.4/test/filters/test_pandoc.rb0000644000004100000410000000217612665031555020143 0ustar www-datawww-dataclass Nanoc::Filters::PandocTest < Nanoc::TestCase
def test_filter
if_have 'pandoc-ruby' do
skip_unless_have_command 'pandoc'
# Create filter
filter = ::Nanoc::Filters::Pandoc.new
# Run filter
result = filter.setup_and_run("# Heading\n")
assert_match(%r{Heading
\s*}, result)
end
end
def test_params_old
if_have 'pandoc-ruby' do
skip_unless_have_command 'pandoc'
# Create filter
filter = ::Nanoc::Filters::Pandoc.new
# Run filter
args = { f: :markdown, to: :html }
result = filter.setup_and_run("# Heading\n", args)
assert_match(%r{Heading
\s*}, result)
end
end
def test_params_new
if_have 'pandoc-ruby' do
skip_unless_have_command 'pandoc'
# Create filter
filter = ::Nanoc::Filters::Pandoc.new
# Run filter
args = [:s, { f: :markdown, to: :html }, 'no-wrap', :toc]
result = filter.setup_and_run("# Heading\n", args: args)
assert_match '', result
assert_match(%r{Heading
\s*}, result)
end
end
end
nanoc-4.1.4/test/filters/test_rainpress.rb0000644000004100000410000000113612665031555020700 0ustar www-datawww-dataclass Nanoc::Filters::RainpressTest < Nanoc::TestCase
def test_filter
if_have 'rainpress' do
# Create filter
filter = ::Nanoc::Filters::Rainpress.new
# Run filter
result = filter.setup_and_run('body { color: black; }')
assert_equal('body{color:#000}', result)
end
end
def test_filter_with_options
if_have 'rainpress' do
# Create filter
filter = ::Nanoc::Filters::Rainpress.new
# Run filter
result = filter.setup_and_run('body { color: #aabbcc; }', colors: false)
assert_equal('body{color:#aabbcc}', result)
end
end
end
nanoc-4.1.4/test/filters/test_markaby.rb0000644000004100000410000000045012665031555020316 0ustar www-datawww-dataclass Nanoc::Filters::MarkabyTest < Nanoc::TestCase
def test_filter
if_have 'markaby' do
# Create filter
filter = ::Nanoc::Filters::Markaby.new
# Run filter
result = filter.setup_and_run("html do\nend")
assert_equal('', result)
end
end
end
nanoc-4.1.4/test/filters/test_rdiscount.rb0000644000004100000410000000141612665031555020705 0ustar www-datawww-dataclass Nanoc::Filters::RDiscountTest < Nanoc::TestCase
def test_filter
if_have 'rdiscount' do
# Create filter
filter = ::Nanoc::Filters::RDiscount.new
# Run filter
result = filter.setup_and_run('> Quote')
assert_match(/\s*Quote<\/p>\s*<\/blockquote>/, result)
end
end
def test_with_extensions
if_have 'rdiscount' do
# Create filter
filter = ::Nanoc::Filters::RDiscount.new
# Run filter
input = "The quotation 'marks' sure make this look sarcastic!"
output_expected = /The quotation ‘marks’ sure make this look sarcastic!/
output_actual = filter.setup_and_run(input, extensions: [:smart])
assert_match(output_expected, output_actual)
end
end
end
nanoc-4.1.4/test/filters/test_rdoc.rb0000644000004100000410000000043412665031555017621 0ustar www-datawww-dataclass Nanoc::Filters::RDocTest < Nanoc::TestCase
def test_filter
# Get filter
filter = ::Nanoc::Filters::RDoc.new
# Run filter
result = filter.setup_and_run('= Foo')
assert_match(%r{\A\s*
Foo(.*)?\s*\Z}, result)
end
end
nanoc-4.1.4/test/filters/test_less.rb0000644000004100000410000000765612665031555017655 0ustar www-datawww-dataclass Nanoc::Filters::LessTest < Nanoc::TestCase
def test_filter
if_have 'less' do
# Create item
@item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('blah', { content_filename: 'content/foo/bar.txt' }, '/foo/bar/'), nil)
# Create filter
filter = ::Nanoc::Filters::Less.new(item: @item, items: [@item])
# Run filter
result = filter.setup_and_run('.foo { bar: 1 + 1 }')
assert_match(/\.foo\s*\{\s*bar:\s*2;?\s*\}/, result)
end
end
def test_filter_with_paths_relative_to_site_directory
if_have 'less' do
# Create file to import
FileUtils.mkdir_p('content/foo/bar')
File.open('content/foo/bar/imported_file.less', 'w') { |io| io.write('p { color: red; }') }
# Create item
@item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('blah', { content_filename: 'content/foo/bar.txt' }, '/foo/bar/'), nil)
# Create filter
filter = ::Nanoc::Filters::Less.new(item: @item, items: [@item])
# Run filter
result = filter.setup_and_run('@import "content/foo/bar/imported_file.less";')
assert_match(/p\s*\{\s*color:\s*red;?\s*\}/, result)
end
end
def test_filter_with_paths_relative_to_current_file
if_have 'less' do
# Create file to import
FileUtils.mkdir_p('content/foo/bar')
File.open('content/foo/bar/imported_file.less', 'w') { |io| io.write('p { color: red; }') }
# Create item
File.open('content/foo/bar.txt', 'w') { |io| io.write('meh') }
@item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('blah', { content_filename: 'content/foo/bar.txt' }, '/foo/bar/'), nil)
# Create filter
filter = ::Nanoc::Filters::Less.new(item: @item, items: [@item])
# Run filter
result = filter.setup_and_run('@import "bar/imported_file.less";')
assert_match(/p\s*\{\s*color:\s*red;?\s*\}/, result)
end
end
def test_recompile_includes
if_have 'less' do
with_site do |site|
# Create two less files
Dir['content/*'].each { |i| FileUtils.rm(i) }
File.open('content/a.less', 'w') do |io|
io.write('@import "b.less";')
end
File.open('content/b.less', 'w') do |io|
io.write('p { color: red; }')
end
# Update rules
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write " filter :less\n"
io.write "end\n"
io.write "\n"
io.write "route '/a/' do\n"
io.write " item.identifier.chop + '.css'\n"
io.write "end\n"
io.write "\n"
io.write "route '/b/' do\n"
io.write " nil\n"
io.write "end\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert Dir['output/*'].size == 1
assert File.file?('output/a.css')
refute File.file?('output/b.css')
assert_match(/^p\s*\{\s*color:\s*red;?\s*\}/, File.read('output/a.css'))
# Update included file
File.open('content/b.less', 'w') do |io|
io.write('p { color: blue; }')
end
# Recompile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Recheck
assert Dir['output/*'].size == 1
assert File.file?('output/a.css')
refute File.file?('output/b.css')
assert_match(/^p\s*\{\s*color:\s*blue;?\s*\}/, File.read('output/a.css'))
end
end
end
def test_compression
if_have 'less' do
# Create item
@item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('blah', { content_filename: 'content/foo/bar.txt' }, '/foo/bar/'), nil)
# Create filter
filter = ::Nanoc::Filters::Less.new(item: @item, items: [@item])
# Run filter with compress option
result = filter.setup_and_run('.foo { bar: a; } .bar { foo: b; }', compress: true)
assert_match(/^\.foo\{bar:a\}\n?\.bar\{foo:b\}/, result)
end
end
end
nanoc-4.1.4/test/filters/test_sass.rb0000644000004100000410000002113712665031555017646 0ustar www-datawww-dataclass Nanoc::Filters::SassTest < Nanoc::TestCase
def setup
super
if_have 'sass' do
unless ::Sass.load_paths.include?('.')
::Sass.load_paths << '.'
end
end
end
def test_filter
if_have 'sass' do
# Get filter
filter = create_filter({ foo: 'bar' })
# Run filter
result = filter.setup_and_run(".foo #bar\n color: #f00")
assert_match(/.foo\s+#bar\s*\{\s*color:\s+(red|#f00);?\s*\}/, result)
end
end
def test_filter_with_params
if_have 'sass' do
# Create filter
filter = create_filter({ foo: 'bar' })
# Check with compact
result = filter.setup_and_run(".foo #bar\n color: #f00", style: 'compact')
assert_match(/^\.foo #bar[\s]*\{[\s]*color:\s*(red|#f00);?[\s]*\}/m, result)
# Check with compressed
result = filter.setup_and_run(".foo #bar\n color: #f00", style: 'compressed')
assert_match(/^\.foo #bar[\s]*\{[\s]*color:\s*(red|#f00);?[\s]*\}/m, result)
end
end
def test_filter_error
if_have 'sass' do
# Create filter
filter = create_filter
# Run filter
raised = false
begin
filter.setup_and_run('$*#&!@($')
rescue Sass::SyntaxError => e
assert_match ':1', e.backtrace[0]
raised = true
end
assert raised
end
end
def test_filter_can_import_external_files
if_have 'sass' do
# Create filter
filter = create_filter
# Create sample file
File.open('moo.sass', 'w') { |io| io.write "body\n color: red" }
# Run filter
filter.setup_and_run('@import moo')
end
end
def test_filter_can_import_relative_files
if_have 'sass' do
# Create filter
filter = create_filter
# Create sample file
File.open('moo.sass', 'w') { |io| io.write %(@import subdir/relative) }
FileUtils.mkdir_p('subdir')
File.open('subdir/relative.sass', 'w') { |io| io.write "body\n color: red" }
# Run filter
filter.setup_and_run('@import moo')
end
end
def test_filter_will_skip_items_without_filename
if_have 'sass' do
# Create filter
filter = create_filter
# Create sample file
File.open('moo.sass', 'w') { |io| io.write "body\n color: red" }
# Run filter
filter.setup_and_run('@import moo')
end
end
def test_css_imports_work
if_have 'sass' do
# Create filter
filter = create_filter
# Run filter
filter.setup_and_run('@import moo.css')
end
end
def test_recompile_includes
if_have 'sass' do
with_site do |site|
# Create two Sass files
Dir['content/*'].each { |i| FileUtils.rm(i) }
File.open('content/a.sass', 'w') do |io|
io.write('@import b.sass')
end
File.open('content/b.sass', 'w') do |io|
io.write("p\n color: red")
end
# Update rules
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write " filter :sass\n"
io.write "end\n"
io.write "\n"
io.write "route '/a/' do\n"
io.write " item.identifier.chop + '.css'\n"
io.write "end\n"
io.write "\n"
io.write "route '/b/' do\n"
io.write " nil\n"
io.write "end\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert Dir['output/*'].size == 1
assert File.file?('output/a.css')
refute File.file?('output/b.css')
assert_match(/^p\s*\{\s*color:\s*red;?\s*\}/, File.read('output/a.css'))
# Update included file
File.open('content/b.sass', 'w') do |io|
io.write("p\n color: blue")
end
# Recompile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Recheck
assert Dir['output/*'].size == 1
assert File.file?('output/a.css')
refute File.file?('output/b.css')
assert_match(/^p\s*\{\s*color:\s*blue;?\s*\}/, File.read('output/a.css'))
end
end
end
def test_recompile_includes_with_underscore_without_extension
if_have 'sass' do
with_site do |site|
# Create two Sass files
Dir['content/*'].each { |i| FileUtils.rm(i) }
File.open('content/a.sass', 'w') do |io|
io.write('@import b')
end
File.open('content/_b.sass', 'w') do |io|
io.write("p\n color: red")
end
# Update rules
File.open('Rules', 'w') do |io|
io.write "compile '*' do\n"
io.write " filter :sass\n"
io.write "end\n"
io.write "\n"
io.write "route '/a/' do\n"
io.write " item.identifier.chop + '.css'\n"
io.write "end\n"
io.write "\n"
io.write "route '/_b/' do\n"
io.write " nil\n"
io.write "end\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
assert Dir['output/*'].size == 1
assert File.file?('output/a.css')
refute File.file?('output/b.css')
assert_match(/^p\s*\{\s*color:\s*red;?\s*\}/, File.read('output/a.css'))
# Update included file
File.open('content/_b.sass', 'w') do |io|
io.write("p\n color: blue")
end
# Recompile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Recheck
assert Dir['output/*'].size == 1
assert File.file?('output/a.css')
refute File.file?('output/b.css')
assert_match(/^p\s*\{\s*color:\s*blue;?\s*\}/, File.read('output/a.css'))
end
end
end
def test_recompile_includes_with_relative_path
if_have 'sass', 'compass' do
with_site do |site|
# Write compass config
FileUtils.mkdir_p('compass')
File.open('compass/config.rb', 'w') do |io|
io << "project_path = \".\"\n"
io << "sass_path = \"content/style\"\n"
end
# Create two Sass files
Dir['content/*'].each { |i| FileUtils.rm(i) }
FileUtils.mkdir_p('content/style/super')
FileUtils.mkdir_p('content/style/sub')
File.open('content/style/super/main.sass', 'w') do |io|
io.write('@import sub/include.sass')
end
File.open('content/style/sub/include.sass', 'w') do |io|
io.write("p\n color: red")
end
# Update rules
File.open('Rules', 'w') do |io|
io.write "require 'compass'\n"
io.write "Compass.add_project_configuration 'compass/config.rb'\n"
io.write "\n"
io.write "compile '*' do\n"
io.write " filter :sass, Compass.sass_engine_options\n"
io.write "end\n"
io.write "\n"
io.write "route '/style/super/main/' do\n"
io.write " item.identifier.chop + '.css'\n"
io.write "end\n"
io.write "\n"
io.write "route '/style/sub/include/' do\n"
io.write " nil\n"
io.write "end\n"
end
# Compile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Check
output_files = Dir['output/**/*'].select { |f| File.file?(f) }
assert_equal ['output/style/super/main.css'], output_files
assert_match(/^p\s*\{\s*color:\s*red;?\s*\}/, File.read('output/style/super/main.css'))
# Update included file
File.open('content/style/sub/include.sass', 'w') do |io|
io.write("p\n color: blue")
end
# Recompile
site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile
# Recheck
output_files = Dir['output/**/*'].select { |f| File.file?(f) }
assert_equal ['output/style/super/main.css'], output_files
assert_match(/^p\s*\{\s*color:\s*blue;?\s*\}/, File.read('output/style/super/main.css'))
end
end
end
def test_sass_without_filter
if_have 'sass' do
File.open('_morestuff.sass', 'w') do |io|
io.write("p\n color: blue")
end
options = { filename: File.join(Dir.getwd, 'test.sass') }
::Sass::Engine.new('@import "morestuff"', options).render
end
end
private
def create_filter(params = {})
FileUtils.mkdir_p('content')
File.open('content/xyzzy.sass', 'w') { |io| io.write('p\n color: green') }
items = [
Nanoc::ItemWithRepsView.new(
Nanoc::Int::Item.new(
'blah',
{ content_filename: 'content/xyzzy.sass' },
'/blah/',
),
nil,
),
]
params = { item: items[0], items: items }.merge(params)
::Nanoc::Filters::Sass.new(params)
end
end
nanoc-4.1.4/test/filters/test_asciidoc.rb0000644000004100000410000000047612665031555020456 0ustar www-datawww-dataclass Nanoc::Filters::AsciiDocTest < Nanoc::TestCase
def test_filter
skip_unless_have_command 'asciidoc'
# Create filter
filter = ::Nanoc::Filters::AsciiDoc.new
# Run filter
result = filter.setup_and_run('== Blah blah')
assert_match %r{
Blah blah
}, result
end
end
nanoc-4.1.4/test/filters/test_mustache.rb0000644000004100000410000000203412665031555020501 0ustar www-datawww-dataclass Nanoc::Filters::MustacheTest < Nanoc::TestCase
def test_filter
if_have 'mustache' do
# Create item
item = Nanoc::Int::Item.new(
'content',
{ title: 'Max Payne', protagonist: 'Max Payne' },
'/games/max-payne/',
)
# Create filter
filter = ::Nanoc::Filters::Mustache.new({ item: item })
# Run filter
result = filter.setup_and_run('The protagonist of {{title}} is {{protagonist}}.')
assert_equal('The protagonist of Max Payne is Max Payne.', result)
end
end
def test_filter_with_yield
if_have 'mustache' do
# Create item
item = Nanoc::Int::Item.new(
'content',
{ title: 'Max Payne', protagonist: 'Max Payne' },
'/games/max-payne/',
)
# Create filter
filter = ::Nanoc::Filters::Mustache.new(
{ content: 'No Payne No Gayne', item: item })
# Run filter
result = filter.setup_and_run('Max says: {{yield}}.')
assert_equal('Max says: No Payne No Gayne.', result)
end
end
end
nanoc-4.1.4/test/filters/test_redcarpet.rb0000644000004100000410000000632712665031555020652 0ustar www-datawww-dataclass Nanoc::Filters::RedcarpetTest < Nanoc::TestCase
def test_find
if_have 'redcarpet' do
refute Nanoc::Filter.named(:redcarpet).nil?
end
end
def test_filter
if_have 'redcarpet' do
# Create filter
filter = ::Nanoc::Filters::Redcarpet.new
# Run filter
result = filter.setup_and_run('> Quote')
assert_match(/\s*Quote<\/p>\s*<\/blockquote>/, result)
end
end
def test_with_extensions
if_have 'redcarpet' do
# Create filter
filter = ::Nanoc::Filters::Redcarpet.new
# Run filter
if ::Redcarpet::VERSION > '2'
input = 'this is ~~good~~ bad'
output_expected = /this is good<\/del> bad/
output_actual = filter.setup_and_run(input, options: { strikethrough: true })
else
input = "The quotation 'marks' sure make this look sarcastic!"
output_expected = /The quotation ‘marks’ sure make this look sarcastic!/
output_actual = filter.setup_and_run(input, options: [:smart])
end
assert_match(output_expected, output_actual)
end
end
def test_html_by_default
if_have 'redcarpet' do
# Create filter
filter = ::Nanoc::Filters::Redcarpet.new
# Run filter
input = ""
output_expected = %r{
}
output_actual = filter.setup_and_run(input)
assert_match(output_expected, output_actual)
end
end
def test_xhtml_if_requested
if_have 'redcarpet' do
# Create filter
filter = ::Nanoc::Filters::Redcarpet.new
# Run filter
input = ""
output_expected = %r{
}
output_actual =
if ::Redcarpet::VERSION > '2'
filter.setup_and_run(input, renderer_options: { xhtml: true })
else
filter.setup_and_run(input, options: [:xhtml])
end
assert_match(output_expected, output_actual)
end
end
def test_html_toc
if_have 'redcarpet' do
unless ::Redcarpet::VERSION > '2'
skip 'Requires Redcarpet >= 2'
end
# Create filter
filter = ::Nanoc::Filters::Redcarpet.new
# Run filter
input = "# Heading 1\n## Heading 2\n"
output_actual = filter.run(input, renderer: Redcarpet::Render::HTML_TOC)
# Test
output_expected = %r{
}
assert_match(output_expected, output_actual)
end
end
def test_toc_if_requested
if_have 'redcarpet' do
# Create filter
filter = ::Nanoc::Filters::Redcarpet.new
# Run filter
input = "A Title\n======"
if ::Redcarpet::VERSION > '2'
output_expected = %r{\n- \nA Title\n
\n
\nA Title
\n}
output_actual = filter.setup_and_run(input, with_toc: true)
else
output_expected = %r{A Title
\n}
output_actual = filter.setup_and_run(input)
end
# Test
assert_match(output_expected, output_actual)
end
end
end
nanoc-4.1.4/test/filters/test_kramdown.rb0000644000004100000410000000131112665031555020507 0ustar www-datawww-dataclass Nanoc::Filters::KramdownTest < Nanoc::TestCase
def test_filter
if_have 'kramdown' do
# Create filter
filter = ::Nanoc::Filters::Kramdown.new
# Run filter
result = filter.setup_and_run('This is _so_ **cool**!')
assert_equal("This is so cool!
\n", result)
end
end
def test_warnings
if_have 'kramdown' do
# Create filter
filter = ::Nanoc::Filters::Kramdown.new
# Run filter
io = capturing_stdio do
filter.setup_and_run('{:foo}this is bogus')
end
assert_empty io[:stdout]
assert_equal "kramdown warning: Found span IAL after text - ignoring it\n", io[:stderr]
end
end
end
nanoc-4.1.4/test/filters/test_rubypants.rb0000644000004100000410000000045612665031555020725 0ustar www-datawww-dataclass Nanoc::Filters::RubyPantsTest < Nanoc::TestCase
def test_filter
if_have 'rubypants' do
# Get filter
filter = ::Nanoc::Filters::RubyPants.new
# Run filter
result = filter.setup_and_run('Wait---what?')
assert_equal('Wait—what?', result)
end
end
end
nanoc-4.1.4/test/filters/test_erb.rb0000644000004100000410000000551612665031555017450 0ustar www-datawww-dataclass Nanoc::Filters::ERBTest < Nanoc::TestCase
def test_filter_with_instance_variable
# Create filter
filter = ::Nanoc::Filters::ERB.new({ location: 'a cheap motel' })
# Run filter
result = filter.setup_and_run('<%= "I was hiding in #{@location}." %>')
assert_equal('I was hiding in a cheap motel.', result)
end
def test_filter_with_instance_method
# Create filter
filter = ::Nanoc::Filters::ERB.new({ location: 'a cheap motel' })
# Run filter
result = filter.setup_and_run('<%= "I was hiding in #{location}." %>')
assert_equal('I was hiding in a cheap motel.', result)
end
def test_filter_error_item
# Create item and item rep
item = MiniTest::Mock.new
item.expect(:identifier, '/foo/bar/baz/')
item_rep = MiniTest::Mock.new
item_rep.expect(:name, :quux)
# Create filter
filter = ::Nanoc::Filters::ERB.new(
item: item,
item_rep: item_rep,
location: 'a cheap motel',
)
# Run filter
raised = false
begin
filter.setup_and_run('<%= this isn\'t really ruby so it\'ll break, muahaha %>')
rescue SyntaxError => e
e.message =~ /(.+?):\d+: /
assert_match 'item /foo/bar/baz/ (rep quux)', Regexp.last_match[1]
raised = true
end
assert raised
end
def test_filter_with_yield
# Create filter
filter = ::Nanoc::Filters::ERB.new({ content: 'a cheap motel' })
# Run filter
result = filter.setup_and_run('<%= "I was hiding in #{yield}." %>')
assert_equal('I was hiding in a cheap motel.', result)
end
def test_filter_with_yield_without_content
# Create filter
filter = ::Nanoc::Filters::ERB.new({ location: 'a cheap motel' })
# Run filter
assert_raises LocalJumpError do
filter.setup_and_run('<%= "I was hiding in #{yield}." %>')
end
end
def test_safe_level
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
skip 'JRuby does not implement safe levels'
end
# Set up
filter = ::Nanoc::Filters::ERB.new
File.open('moo', 'w') { |io| io.write('one miiillion dollars') }
# Without
res = filter.setup_and_run('<%= File.read("moo") %>', safe_level: nil)
assert_equal 'one miiillion dollars', res
# With
assert_raises(SecurityError) do
res = filter.setup_and_run('<%= eval File.read("moo") %>', safe_level: 1)
end
end
def test_trim_mode
# Set up
filter = ::Nanoc::Filters::ERB.new({ location: 'a cheap motel' })
$trim_mode_works = false
# Without
filter.setup_and_run('% $trim_mode_works = true')
refute $trim_mode_works
# With
filter.setup_and_run('% $trim_mode_works = true', trim_mode: '%')
assert $trim_mode_works
end
def test_locals
filter = ::Nanoc::Filters::ERB.new
result = filter.setup_and_run('<%= @local %>', locals: { local: 123 })
assert_equal '123', result
end
end
nanoc-4.1.4/test/filters/test_relativize_paths.rb0000644000004100000410000004636412665031555022263 0ustar www-datawww-dataclass Nanoc::Filters::RelativizePathsTest < Nanoc::TestCase
def test_filter_html_with_double_quotes
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/foo/bar/baz/'
end
# Set content
raw_content = %(foo)
expected_content = %(foo)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_with_single_quotes
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/foo/bar/baz/'
end
# Set content
raw_content = %(foo)
expected_content = %(foo)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_without_quotes
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/foo/bar/baz/'
end
# Set content
raw_content = %(foo)
expected_content = %(foo)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_with_boilerplate
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/foo/bar/baz/'
end
# Set content
raw_content = <
Hello
foo
EOS
expected_match_0 = %r{foo}
expected_match_1 = %r{\A\s*\s*\s*(.|\s)*Hello \s*\s*\s*foo\s*\s*\s*\Z}m
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_match(expected_match_0, actual_content)
assert_match(expected_match_1, actual_content)
end
def test_filter_html_multiple
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/foo/bar/baz/'
end
# Set content
raw_content = %(foo bar)
expected_content = %(foo bar)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_nested
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/foo/bar/baz/'
end
# Set content
raw_content = %(
)
expected_content = %(
)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_outside_tag
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/foo/bar/baz/'
end
# Set content
raw_content = %(stuff href="/foo" more stuff)
expected_content = %(stuff href="/foo" more stuff)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_root
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/woof/meow/'
end
# Set content
raw_content = %(foo)
expected_content = %(foo)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_network_path
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/woof/meow/'
end
# Set content
raw_content = %(example.com)
expected_content = %(example.com)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_with_anchor
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/woof/meow/'
end
# Set content
raw_content = %(Max Payne)
expected_content = %(Max Payne)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_with_url
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/woof/meow/'
end
# Set content
raw_content = %(Example)
expected_content = %(Example)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_with_relative_path
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/woof/meow/'
end
# Set content
raw_content = %(Example)
expected_content = %(Example)
# Test
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_equal(expected_content, actual_content)
end
def test_filter_html_object_with_relative_path
# Create filter with mock item
filter = Nanoc::Filters::RelativizePaths.new
# Mock item
filter.instance_eval do
@item_rep = Nanoc::Int::ItemRep.new(
Nanoc::Int::Item.new(
'content',
{},
'/foo/bar/baz/'),
:blah)
@item_rep.paths[:last] = '/woof/meow/'
end
raw_content = %()
actual_content = filter.setup_and_run(raw_content, type: :html)
assert_match(/