nanoc-4.1.4/0000755000004100000410000000000012665031555012656 5ustar www-datawww-datananoc-4.1.4/Rakefile0000644000004100000410000000007212665031555014322 0ustar www-datawww-dataRake.add_rakelib 'tasks' task default: [:test, :rubocop] nanoc-4.1.4/NEWS.md0000644000004100000410000011417412665031555013764 0ustar www-datawww-data# Nanoc news ## 4.1.4 (2016-02-13) Fixes: * Added missing `Configuration#key?` method (#815, #820) * Made output diff use correct snapshot rather than `:last` (#813, #814) Enhancements: * Sped up item resolution in Sass filter (#821) * Made `#link_to` more resilient to unsupported argument types (#816, #819) ## 4.1.3 (2016-01-30) Fixes: * Fixed crash in `check` command when the subject of an issue is nil (#804, #811) * Made stale check not ignore non-final snapshot paths (#809, #810) ## 4.1.2 (2016-01-16) Fixes: * Made @-variables (e.g. `@items`) report their frozenness properly, so that optimisations based on frozenness work once again (#795, #797) * Removed environment from `crash.log` to prevent leaking sensitive information (#798, #800) Enhancements: * Removed redundant checksum calculation (#789) [Ruben Verborgh] ## 4.1.1 (2015-12-30) Fixes: * Fixed preprocessor not being run before check/deploy/prune commands (#763, #784, #787, #788) Enhancements: * Made `#breadcrumbs_trail` explicitly fail when using full identifiers (#781, #783) ## 4.1.0 (2015-12-18) Fixes: * Fixed crash when attempting to `#puts` an object that’s not a string (#778) * Made pruner not prune away files from routes defined for custom snapshots (#779) * Wrapped `@layout` in a layout view (#773) Enhancements: * Added a base path to the Checks file, so that it supports `#require_relative` (#774) ## 4.1.0rc2 (2015-12-13) Fixes: * Fixed children of the root item not having a parent (#769, #770) Enhancements: * Made `#path`, `#compiled_content` and `#reps` unavailable during pre-processing, compilation and routing, because they do not make sense in these contexts (#571, #767, #768) ## 4.1.0rc1 (2015-12-12) Fixes: * Fixed `@item.compiled_content` in a layout raising an exception (#761, #766) ## 4.1.0b1 (2015-12-11) Fixes: * Fixed issue with `:pre` snapshot not being generated properly (#764) Enhancements: * Updated default site to use `#write` (#759) ## 4.1.0a1 (2015-12-05) Features: * Added `postprocess` block (#726) [Garen Torikian] * Added `#write` compilation instruction and `path` option to `#snapshot` (#753) Fixes: * Fixed crash when printing non-string object (#712) [Garen Torikian] * Removed English text from `#link_to` helper (#736) [Lucas Vuotto] Enhancements: * Allowed excluding URLs from external links check (#686) [Yannick Ihmels] * Added `atom` to list of text extensions (#657) [Yannick Ihmels] * Added `#each` to `Nanoc::ConfigView` (#705) [Garen Torikian] * Made `#attribute_to_time` handle `DateTime` (#717) [Micha Rosenbaum] * Added `Identifier#components` (#677) * Added `:existing` option to `#content_for` (can be `:error`, `:overwrite` and `:append`) (#744) ## 4.0.2 (2015-11-30) Fixes: * Properly set required Ruby version to 2.1 in the gem specification (#747) * Fixed issue with CLI commands not being loaded as UTF-8 (#742) * Added missing `#identifier=` method to items and layouts during preprocessing (#750) Enhancements: * Let attempts to fetch an item rep by number, rather than symbol, fail with a meaningful error (#749) ## 4.0.1 (2015-11-28) Fixes: * Fixed params documentation for :rdiscount filter (#722) * Fixed crash when comparing item rep views (#735, #738) Enhancements: * Lowered minimum required Ruby version from 2.2 to 2.1 (#732) ## 4.0.0 (2015-11-07) Enhancements: * `#parent` and `#children` now raise an exception when used on items with a non-legacy identifier (#710) ## 4.0.0rc3 (2015-09-20) Features: * Added `Identifier#without_exts` and `Identifier#exts` (#644, #696) [Rémi Barraquand] * Added `DocumentView#attributes` (#699, #702) Fixes: * Fixed issue when comparing document views (#680, #693) Enhancements: * Made `#base_url` argument in `#tags_for` optional (#687) [Croath Liu] * Allowed `IdentifiableCollection#[]` to be passed an identifier (#681, #695) * Improved `Pattern.from` error message (#683, #692) * Let default site use a direct link to the stylesheet (#685, #701) Changes: * Removed `Identifier#with_ext` because its behavior was confusing (#697, #700) * Disallowed storing document (views) in attributes (#682, #694) ## 4.0.0rc2 (2015-07-11) Fixes: * Fixed broken `shell` command (#672) [Jim Mendenhall] * Fixed absolute path check on Windows (#656) Enhancements: * Made Nanoc error when multiple items have the same output path (#665, #669) * Improved error message for non-hash frontmatter (#670, #673) Changes: * nanoc is now called Nanoc ## 4.0.0rc1 (2015-06-21) Fixes: * Fixed double-wrapping of `@layout` in rendering helper (#631) * Fixed `show-rules` command (#633) ## 4.0.0b4 (2015-06-10) Fixes: * Added missing `#ext` method to identifiers (#612) * Fixed issue where identifiers would have the wrong extension (#611) * Fixed rule context exposing entities rather than views (#614, #615) * Fixed `#key?` and `#fetch` not being available on layout views (#618) * Fixed `#update_attributes` not being available on mutable layout views (#619) ## 4.0.0b3 (2015-05-31) Changes: * Removed `filesystem_verbose` data source (#599) * Set minimum required Ruby version to 2.2 Enhancements: * Made `@config`, `@items` and `@layouts` available in checks (#598) * Made `filesystem` an alias for `filesystem_unified` (#599) * Made specific reps for an item accessible using `@item.reps[:name]` (#586, #607) * Removed `allow_periods_in_identifiers` documentation (#605) * Made fog deployer not upload files with identical ETags to AWS (#480, #536, #552) [Paul Boone] Fixes: * Made `ItemView#parent` return nil if parent is nil (#600, #602) * Added missing `identifier_type` documentation (#604) ## 4.0.0b2 (2015-05-23) Changes: * Removed `ItemCollectionView#at` (#582) * Removed support for calling `ItemCollectionView#[]` with an integer (#582) * Renamed `identifier_style` to `identifier_type`, and made its values be `"full"` or `"legacy"` (#593) * Renamed `pattern_syntax` to `string_pattern_type`, and made its values be `"glob"` or `"legacy"` (#593) * Made `"full"` the default for `identifier_type` (#592, #594) * Made `"glob"` the default for `string_pattern_type` (#592) * Enabled auto-pruning by default for new sites (#590) Enhancements: * Added `--force` to `create-site` command (#580) [David Alexander] * Made default Rules file more future-proof (#591) Fixes: * Fixed `LayoutCollectionView#[]` documentation (it mentioned items) * Fixed `ItemCollectionView#[]` returning an array when passed a regex * Fixed an issue with mutable collection views’ `#delete_if` not yielding mutable views * Fixed an issue with collection views’ `#find_all` returning entities instead of views ## 4.0.0b1 (2015-05-14) Changes: * Removed tasks * Removed several private methods in the view API * Removed default `base_url` in tagging helper Enhancements: * Removed unused options from CLI * Added `Nanoc::Identifier#without_ext` * Made `Nanoc::Identifier#=~` work with a glob * Added `Nanoc::LayoutCollectionView#[]` * Allowed creation of site in current directory (#549) [David Alexander] Fixes: * Fixed `#passthrough` for identifiers with extensions * Fixed rendering helper for identifiers with extensions * Fixed filtering helper ## 4.0.0a2 (2015-05-12) Features: * Glob patterns (opt-in by setting `pattern_syntax` to `"glob"` in the site configuration) * Identifiers with extensions (opt-in by setting `identifier_style` to `"full"` in the data source configuration) Enhancements: * Added several convenience methods to view classes (#570, #572) See the [nanoc 4 upgrade guide](http://nanoc.ws/docs/nanoc-4-upgrade-guide/) for details. ## 4.0.0a1 (2015-05-09) This is a major upgrade. For details on upgrading, see the [nanoc 4 upgrade guide](http://nanoc.ws/docs/nanoc-4-upgrade-guide/). This release provides no new features, but streamlines the API and functionality, in order to easen future development, both for features and for optimisations. ## 3.8.0 (2015-05-04) Features: * Added `mixed_content` check (#542, #543) [Mike Pennisi] * Added `commands_dirs` configuration option for specifying directories to read commands from (#475) [Gregory Pakosz] * Added `:cdn_id` option to fog deployer for invalidating CDN objects (#451) [Vlatko Kosturjak] * Add access to regular expressions group matches in rules (#478) [Michal Papis] * Allow filtering the items array by regex (#458) [Mike Pennisi] Enhancements: * Added `:preserve_order` option to preserve order in Atom feed (#533, #534) * Allowed accessing `:pre` snapshot from within item itself (#537, #538, #548) Fixes: * Allowed passing generic Pandoc options with :args (#526, #535) * Fix crash when compiling extensionless binary items (#524, #525) * Fix double snapshot creation error (#547) ## 3.7.5 (2015-01-12) Enhancements: * Allowed extra patterns to be specified in the data source configuration, so that dotfiles are no longer necessary ignored (e.g. `extra_files: ['.htaccess']`) (#492, #498) [Andy Drop, Michal Papis] * Removed Ruby 1.8.x support ([details](https://groups.google.com/forum/#!topic/nanoc/pSL1i15EFz8)) (#517) * Improved CSS and HTML error messages (#484, #504) * Let kramdown filter print warnings (#459, #519) Fixes: * Fixed HTML class names for recent Rouge versions (#502) * Fixed crash when using items or layouts in attributes (#469, #518) ## 3.7.4 (2014-11-23) Enhancements: * Made `check` command fail when output directory is missing (#472) [Mike Pennisi] * Made external links check timeouts start small and grow (#483) [Michal Papis] * Made code and API adhere much more closely to the Ruby style guide (#476) Fixes: * Fixed potential “parent directory is world writable” error (#465, #474) * Fixed retrying requests in the external link checker (#483) [Michal Papis] * Fixed issue with data sources not being unloaded (#491) [Michal Papis] ## 3.7.3 (2014-08-31) Fixes: * Fixed issue which caused metadata sections not be recognised in files that use CRLF line endings (#470, #471) [Gregory Pakosz] ## 3.7.2 (2014-08-17) Fixes: * Fixed broken links to the now defunct RubyForge (#454, #467) * Fixed crash when Gemfile is missing but Bundler is installed (#464) * Made filesystem data source not strip any whitespace (#463) [Gregory Pakosz] Enhancements: * Fixed issue which could cause items to be unnecessarily marked as outdated (#461) [Gregory Pakosz] * Prevented binary layouts from being generated (#468) [Gregory Pakosz] ## 3.7.1 (2014-06-16) Fixes: * Fixed bug which would cause nanoc to crash if no Gemfile is present (#447, #449) ## 3.7.0 (2014-06-08) New features: * Allowed excluding links from the internal links check (`@config[:checks][:internal_links][:exclude]`) (#242) [Remko Tronçon] * Added Rouge syntax coloring filter (#398) [Guilherme Garnier] * Backported `after_setup` from nanoc 4 to make it easier to create CLI plugins (#407) [Rémi Barraquand] * Make lib dirs configurable using `lib_dirs` config attribute (#424) [Gregory Pakosz] * Added support for setting parent config dir using `parent_config_file` config attribute (#419) [Gregory Pakosz] Enhancements: * Added `:with_toc` support to RedCarpet (#222, #232) * Added `slim` to the list of text extensions (#316) * Made `content/` and `layouts/` dirs configurable (#412) [Gregory Pakosz] * Allowed included rules files to have their own preprocess block (#420) [Gregory Pakosz] Fixes: * Fixed bug which caused temporary directories not to be removed (#440, #444) ## 3.6.11 (2014-05-09) Identical to 3.6.10 but published with corrected release notes. This release was previously known as 3.6.10.1, but was renamed due to incompatibilities with the Semantic Versioning specification. ## 3.6.10 (2014-05-09) Fixes: * Fixed occasional "no such file" error on JRuby (#422) * Prevented multiple items and layouts from having the same identifier (#434, #435) Enhancements: * Set default encoding to UTF-8 (#428) * Improved checksummer to reduce number of unnecessary recompiles (#310, #431) * Disabled USR1 on JRuby in order to suppress warning (#425, #426) * Made pandoc filter argument passing more generic (#210, #433) ## 3.6.9 (2014-04-15) Fixes: * Fixed path to default stylesheet (#410, #411) * Improved reliability of piping from/to external processes in JRuby (#417) * Added workaround for “cannot modify” errors when using Nokogiri on JRuby (#416) * Made corrupted cached data auto-repair itself if possible (#409, #418) ## 3.6.8 (2014-03-22) Fixes: * Fixed issue with missing compilation durations (#374, #379) * Made XSL filter transform item rather than layout (#399, #401) [Simon South] * Made XSL filter honor omit-xml-declaration (#403, #404) [Simon South] * Removed "see full crash log" line from crash log (#397, #402) Enhancements: * Added warning when multiple preprocessors are defined (#389) * Improve stylesheet handling in default site (#339, #395) ## 3.6.7 (2013-12-09) Fixes: * Made Handlebars filter usable outside layouts (#346, #348) * Fixed ANSI color support on Windows (#352, #356) * Made fog deployer handle prefixes properly (#351) [Oliver Byford] * Fixed crash in watcher (#358) * Fixed huge durations when showing skipped items after compilation (#360, #364) * Fixed output of `--verbose` compilation statistics (#359, #365) * Fixed issue with Sass files not recompiling (#350, #370) Enhancements: * Fixed Windows compatibility issues in test suite (#353) [Raphael von der Grün] * Hid deprecated `autocompile` and `watch` commands in help * Made CLI swallow broken pipe errors when piping to a process that terminates prematurely (#318, #369) ## 3.6.6 (2013-11-08) Enhancements: * Reduced number of dependencies generated by Sass filter (#306) [Gregory Pakosz] * Recognised lowercase `utf` in language value (e.g. `en_US.utf8`) as being UTF-8 (#335, #338) * Set [Thin](http://code.macournoyer.com/thin/) as the default server for `nanoc view` (#342, #345) * Removed watcher section from the default configuration file (#343, #344) Fixes: * Prevented capturing helper from erroneously compiling items twice (#331, #337) ## 3.6.5 (2013-09-29) Fixes: * Fixed bug which could cause incorrect dependencies to be generated in some cases (#329) * Fixed handling of index filenames when allowing periods in identifiers (#330) ## 3.6.4 (2013-05-29) Enhancements: * Deprecated `watch` and `autocompile` commands in favour of [`guard-nanoc`](https://github.com/nanoc/guard-nanoc) Fixes: * Fixed bug which could cause the `tmp/` dir to blow up in size * Unescaped URLs when checking internal links ## 3.6.3 (2013-04-24) Fixes: * Added support for growlnotify on Windows (#253, #267) * Fixed bug which caused the external links checker to ignore the query string (#279, #297) * Removed weird treatment of `DOCTYPE`s in the `relativize_paths` filter (#296) * Fixed CodeRay syntax coloring on Ruby 2.0 * Silenced "Could not find files for the given pattern(s)" message on Windows (#298) * Fixed issue which could cause `output.diff` not to be generated correctly (#255, #301) * Let filesystem and static data sources follow symlinks (#299, #302) * Added compatibility with Listen 1.0 (#309) * Let `#passthrough` in Rules work well with the static data source (#251) [Gregory Pakosz] * Made timing information be more accurate (#303) ## 3.6.2 (2013-03-23) Fixes: * Removed the list of available deployers from the `deploy` help text and moved them into a new `--list-deployers` option [Damien Pollet] * Fixed warning about `__send__ `and `object_id` being redefined on Ruby 1.8.x [Justin Hileman] Enhancements: * Added possible alternative names for the `Checks` file for consistency with the `Rules` file: `Checks.rb`, `checks`, `checks.rb` [Damien Pollet] * Made sure unchanged files never have their mtime updated [Justin Hileman] * Made link checker retry 405 Method Not Allowed results with GET instead of HEAD [Daniel Hofstetter] ## 3.6.1 (2013-02-25) Fixes: * Fixed bug which could cause the Sass filter to raise a load error [Damien Pollet] * Fixed warnings about `__send__` and `object_id` being redefined [Justin Hileman] * Made `files_to_watch` contain `nanoc.yaml`, not `config.yaml` by default ## 3.6 (2013-02-24) Features: * Added `sync` command, allowing data sources to update local caches of external data [Justin Hileman] * Added `#ignore` compiler DSL method * Allowed accessing items by identifier using e.g. `@items['/about/']` * Added `shell` command Enhancements: * Renamed the nanoc configuration file from `config.yaml` to `nanoc.yaml` Fixes: * Updated references to old web site and old repository * Made `require` errors mention Bundler if appropriate * Fixed bug which caused pruner not to delete directories in some cases [@reima] * Made `check` command exit with the proper exit status * Added support for the `HTML_TOC` Redcarpet renderer * Made `stale` check honor files excluded by the pruner ## 3.5 (2013-01-27) Major changes: * Added checks Minor changes: * Added `#include_rules` for modularising Rules files [Justin Hileman] * Replaced FSSM with Listen [Takashi Uchibe] * Made USR1 print stack trace (not on Windows) * Added ability to configure autocompiler host/port in config.yaml [Stuart Montgomery] * Added static data source * Added `:rep_select` parameter to XML sitemap to allow filtering reps * Removed use of bright/bold colors for compatibility with Solarized Exensions: * Added support for parameters in Less filter [Ruben Verborgh] * Added support for icon and logo in Atom feed [Ruben Verborgh] Fixes: * Made syntax colorizer only use the first non-empty line when extracting the language comment * Fixed XSL filter ## 3.4.3 (2012-12-09) Improvements: * Item reps are now accessible in a consistent way: in Rules and during compilation, they can be accessed using both `@rep` and `@item_rep` Fixes: * Made cleaning streams (stdout/stderr as used by nanoc) compatible with Ruby’s built-in Logger * Made prune work when the output directory is a symlink * Made Handlebars filter compatible with the latest version * Made `show-data` command show more accurate dependencies [Stefan Bühler] * Restored compatibility with Sass 3.2.2 ## 3.4.2 (2012-11-01) Fixes: * Made passthrough rules be inserted in the right place [Gregory Pakosz] * Fixed crashes in the progress indicator when compiling * Made auto-pruning honor excluded files [Greg Karékinian] * Made lack of which/where not crash watch command Improvements: * Fixed constant reinitialization warnings [Damien Pollet] * Made UTF-8 not be decomposed when outputting to a file from a non-UTF-8 terminal * Made syntax colorizer wrap CodeRay output in required CodeRay divs * Made fog delete after upload, not before [Go Maeda] * Made requesting compiled content of binary item impossible ## 3.4.1 (2012-09-22) Fixes: * Fixed auto-pruning * Made slim filter work with the capturing helper [Bil Bas] Improvements: * Made several speed improvements * Added prune configuration to config.yaml * Made nanoc check for presence of nanoc in Gemfile * Made compile command not show identicals (use `--verbose` if you want them) * Made `relativize_paths` filter recognise more paths to relativize [Arnau Siches] * Fixed #passthrough for items without extensions [Justin Hileman] * Added more IO/File proxy methods to cleaning streams ## 3.4 (2012-06-09) * Improved error output and added crash log * Renamed `debug` and `info` commands to `show-data` and `show-plugins`, respectively * Added `show-rules` command (aka `explain`) Extensions: * Added `:yield` key for Mustache filter * Added Handebars filter * Added Pandoc filter * Made the deployer use the `default` target if no target is specified * Converted HTML/CSS/link validation tasks to commands * Made link validator follow relative redirects ## 3.3.7 (2012-05-28) * Added filename to YAML parser errors * Fixed issue which caused extra dependencies to be generated * Made timing information take filtering helper into account ## 3.3.6 (2012-04-27) * Fixed issue with relative_link_to stripping HTML boilerplate ## 3.3.5 (2012-04-23) * Fixed issue with relative_link_to not working properly ## 3.3.4 (2012-04-23) * Fixed bug which caused the compilation stack to be empty * Restored Ruby 1.8 compatibility ## 3.3.3 (2012-04-11) * Fixed directed graph implementation on Rubinius * Made capturing helper not remember content between runs * Fixed Date#freeze issue on Ruby 1.8.x * Made it possible to have any kind of object as parameters in the Rules file * Fixed bug which caused changed routes not to cause a recompile ## 3.3.2 (2012-03-16) * Removed bin/nanoc3 (use nanoc3 gem if you want it) * Fixed wrong “no such snapshot” errors * Made deployer default to rsync for backwards compatibility * Fixed missing Nanoc::CLI in deployment tasks * Fixed “unrecognised kind” deployer error ## 3.3.1 (2012-02-18) * Fixed issue with long paths on Windows * Fixed a few deployer crashes * Added nanoc3.rb, nanoc3/tasks.rb, … for compatibility with older versions * Made nanoc setup Bundler at startup [John Nishinaga] ## 3.3 (2012-02-12) Base: * Dropped the “3” suffix on nanoc3/Nanoc3 * Turned Rake tasks into proper nanoc commands * Improved dependency tracking * Added support for locals in filters and layouts Extensions: * Added support for deployment using Fog [Jack Chu] * Added CoffeeScript filter [Riley Goodside] * Added XSL filter [Arnau Siches] * Added YUICompress filter [Matt Keveney] * Added pygments.rb to supported syntax colorizers * Allowed syntax colorizer to colorize outside `pre` elements [Kevin Lynagh] * Added support for HTTPS link validation [Fabian Buch] * Added support for (automatically) pruning stray output files [Justin Hileman] * Added deploy command ## 3.2.4 (2012-01-09) * Fixed bug which would cause some reps not to be compiled when invoking nanoc programmatically * Made data source configuration location a bit more obvious * Fixed watch command under Windows * Made filesystem data source ignore UTF-8 BOM * Improved compatibility of `colorize_syntax` filter with older libxml versions ## 3.2.3 (2011-10-31) * Made syntax colorizer only strip trailing blank lines instead of all blanks * Improved Ruby 1.9.x compatibility * Made default rakefile require rubygems if necessary * Made filename/content argument of `Nanoc3::Item#initialize` mandatory ## 3.2.2 (2011-09-04) * Fixed command usage printing * Made `relativize_paths` filter handle Windows network paths [Ruben Verborgh] * Made watcher use correct configuration * Allowed code blocks to start with a non-language shebang line ## 3.2.1 (2011-07-27) * Made `@config` available in rules file * Fixed `#readpartial` issue on JRuby [Matt Keveney] * Fixed possible `@cache` name clash in memoization module * Fixed options with required arguments (such as `--port` and `--host`) * Fixed broken `#check_availability` * Fixed error handling in watch command ## 3.2 (2011-07-24) Base: * Sped up nanoc quite a bit * Added progress indicator for long-running filters * Made all source data, such as item attributes, frozen during compilation * Added --color option to force color on * Cleaned up internals, deprecating several parts and/or marking them as private in the progress * Allowed custom commands in commands/ Extensions: * Added AsciiDoc filter * Added Redcarpet filter [Peter Aronoff] * Added Slim filter [Zaiste de Grengolada] * Added Typogruby filter * Added UglifyJS filter [Justin Hileman] * Added `:items` parameter for the XML site map [Justin Hileman] * Added support for params to ERB * Added `:default_colorizer` parameter to the `:colorize_syntax` filter * Allowed for passing arbitrary options to pygmentize [Matthias Vallentin] * Exposed RedCloth parameters in the filter [Vincent Driessen] ## 3.1.9 (2011-06-30) * Really fixed dependency generation between Sass partials this time * Updated Less filter to 2.0 * Made `colorize_syntax` filter throw an error if pygmentize is not available ## 3.1.8 (2011-06-25) * Made link validator accept https: URLs * Fixed erroneous handling of layouts with names ending in index * Fixed dependency generation between Sass partials * Fixed errors related to thread requires * Fixed crash while handling load errors * Improved encoding handling while reading files ## 3.1.7 (2011-05-03) * Restored compatibility with Sass 3.1 ## 3.1.6 (2010-11-21) * Fixed issues with incompatible encodings ## 3.1.5 (2010-08-24) * Improved `#render` documentation * Improved metadata section check so that e.g. raw diffs are handled properly * Deprecated using `Nanoc3::Site#initialize` with a non-`"."` argument * Added Ruby engine to version string * Allowed the `created_at` and `updated_at` attributes used in the `Blogging` helper to be `Date` instances ## 3.1.4 (2010-07-25) * Made INT and TERM signals always quit the CLI * Allowed relative imports in LESS * Made sure modification times are unchanged for identical recompiled items * Improved link validator error handling * Made pygmentize not output extra divs and pres * Allowed colorizers to be specified using symbols instead of strings * Added scss to the default list of text extensions ## 3.1.3 (2010-04-25) * Removed annoying win32console warning [Eric Sunshine] * Removed color codes when not writing to a terminal, or when writing to Windows’ console when win32console is not installed [Eric Sunshine] * Added .xhtml and .xml to list of text extensions * Improved support for relative Sass @imports [Chris Eppstein] ## 3.1.2 (2010-04-07) * Fixed bug which could cause incorrect output when compilation of an item is delayed due to an unmet dependency ## 3.1.1 (2010-04-05) * Sass `@import`s now work for files not managed by nanoc * Rake tasks now have their Unicode description decomposed if necessary ## 3.1 (2010-04-03) New: * An `Item#rep_named(name)` function for quickly getting a certain rep * An `Item#compiled_content` function for quickly getting compiled content * An `Item#path` function for quickly getting the path of an item rep * A new “+” wildcard in rule patterns that matches one or more characters * A `view` command that starts a web server in the output directory * A `debug` command that shows information about the items, reps and layouts * A `kramdown` filter ([kramdown site](http://kramdown.gettalong.org/)) * A diff between the previously compiled content and the last compiled content is now written to `output.diff` if the `enable_output_diff` site configuration attribute is true * Assigns, such as `@items`, `@layouts`, `@item`, … are accessible without `@` * Support for binary items Changed: * New sites now come with a stylesheet item instead of a `style.css` file in the output directory * The `deploy:rsync` task now use sensible default options * The `deploy:rsync` task now accepts a config environment variable * The `deploy:rsync` task now uses a lowercase `dry_run` environment variable * The `maruku` filter now accepts parameters * The `rainpress` filter now accepts parameters * The `filesystem` data source is now known as `filesystem_verbose` * Meta files and content files are now optional * The `filesystem_compact` and `filesystem_combined` data sources have been merged into a new `filesystem_unified` data source * The metadata section in `filesystem_unified` is now optional [Christopher Eppstein] * The `--server` autocompile option is now known as `--handler` * Assigns in filters are now available as instance variables and methods * The `#breadcrumbs_trail` function now allows missing parents * The `sass` filter now properly handles `@import` dependencies Deprecated: * `Nanoc3::FileProxy`; use one of the filename attributes instead * `ItemRep#content_at_snapshot`; use `#compiled_content` instead * The `last_fm`, `delicious` and `twitter` data sources; fetch online content into a cache by a rake task and load data from this cache instead ## 3.0.9 (2010-02-24) * Fixed 1.8.x parsing bug due to lack of parens which could cause “undefined method `to_iso8601_time` for #” errors ## 3.0.8 (2010-02-24) * `#atom_tag_for` now works with `base_url`s that contain a path [Eric Sunshine] * Generated root URLs in `#atom_feed` now end with a slash [Eric Sunshine] * Autocompiler now recognises requests to index files * `Blogging` helper now allows `created_at` to be a Time instance ## 3.0.7 (2010-01-29) * Fixed bug which could cause layout rules not be matched in order ## 3.0.6 (2010-01-17) * Error checking in `filesystem_combined` has been improved [Brian Candler] * Generated HTML files now have a default encoding of UTF-8 * Periods in identifiers for layouts now behave correctly * The `relativize_paths` filter now correctly handles “/” [Eric Sunshine] ## 3.0.5 (2010-01-12) * Restored pre-3.0.3 behaviour of periods in identifiers. By default, a file can have multiple extensions (e.g. `content/foo.html.erb` will have the identifier `/foo/`), but if `allow_periods_in_identifiers` in the site configuration is true, a file can have only one extension (e.g. `content/blog/stuff.entry.html` will have the identifier `/blog/stuff.entry/`). ## 3.0.4 (2010-01-07) * Fixed a bug which would cause the `filesystem_compact` data source to incorrectly determine the content filename, leading to weird “Expected 1 content file but found 3” errors [Eric Sunshine] ## 3.0.3 (2010-01-06) * The `Blogging` helper now properly handles item reps without paths * The `relativize_paths` filter now only operates inside tags * The autocompiler now handles escaped paths * The `LinkTo` and `Tagging` helpers now output escaped HTML * Fixed `played_at` attribute assignment in the `LastFM` data source for tracks playing now, and added a `now_playing` attribute [Nicky Peeters] * The `filesystem_*` data sources can now handle dots in identifiers * Required enumerator to make sure `#enum_with_index` always works * `Array#stringify_keys` now properly recurses ## 3.0.2 (2009-11-07) * Children-only identifier patterns no longer erroneously also match parent (e.g.` /foo/*/` no longer matches `/foo/`) * The `create_site` command no longer uses those ugly HTML entities * Install message now mentions the IRC channel ## 3.0.1 (2009-10-05) * The proper exception is now raised when no matching compilation rules can be found * The autocompile command no longer has a duplicate `--port` option * The `#url_for` and `#feed_url` methods now check the presence of the `base_url` site configuration attribute * Several outdated URLs are now up-to-date * Error handling has been improved in general ## 3.0 (2009-08-14) New: * Multiple data sources * Dependency tracking between items * Filters can now optionally take arguments * `#create_page` and `#create_layout` methods in data sources * A new way to specify compilation/routing rules using a Rules file * A `coderay` filter ([CodeRay site](http://coderay.rubychan.de/)) * A `filesystem_compact` data source which uses less directories Changed: * Pages and textual assets are now known as “items” Removed: * Support for drafts * Support for binary assets * Support for templates * Everything that was deprecated in nanoc 2.x * `save_*`, `move_*` and `delete_*` methods in data sources * Processing instructions in metadata ## 2.2.2 (2009-05-18) * Removed `relativize_paths` filter; use `relativize_paths_in_html` or `relativize_paths_in_css` instead * Fixed bug which could cause nanoc to eat massive amounts of memory when an exception occurs * Fixed bug which would cause nanoc to complain about the open file limit being reached when using a large amount of assets ## 2.2.1 (2009-04-08) * Fixed bug which prevented `relative_path_to` from working * Split `relativize_paths` filter into two filter: `relativize_paths_in_html` and `relativize_paths_in_css` * Removed bundled mime-types library ## 2.2 (2009-04-06) New: * `--pages` and `--assets` compiler options * `--no-color` command-line option * `Filtering` helper * `#relative_path_to` function in `LinkTo` helper * `rainpress` filter ([Rainpress site](http://code.google.com/p/rainpress/)) * `relativize_paths` filter * The current layout is now accessible through the `@layout` variable * Much more informative stack traces when something goes wrong Changed: * The command-line option parser is now a lot more reliable * `#atom_feed` now takes optional `:content_proc`, `:excerpt_proc` and `:articles` parameters * The compile command show non-written items (those with `skip_output: true`) * The compile command compiles everything by default * Added `--only-outdated` option to compile only outdated pages Removed: * deprecated extension-based code ## 2.1.6 (2009-02-28) * The `filesystem_combined` data source now supports empty metadata sections * The `rdoc` filter now works for both RDoc 1.x and 2.x * The autocompiler now serves a 500 when an exception occurs outside compilation * The autocompiler no longer serves index files when the request path does not end with a slash * The autocompiler now always serves asset content correctly ## 2.1.5 (2009-02-01) * Added Ruby 1.9 compatibility * The `filesystem` and `filesystem_combined` data sources now preserve custom extensions ## 2.1.4 (2008-11-15) * Fixed an issue where the autocompiler in Windows would serve broken assets ## 2.1.3 (2008-09-27) * The `haml` and `sass` filters now correctly take their options from assets * The autocompiler now serves index files instead of 404s * Layouts named “index” are now handled correctly * The `filesystem_combined` data source now properly handles assets ## 2.1.2 (2008-09-08) * The utocompiler now compiles assets as well * The `sass` filter now takes options (just like the `haml` filter) * Haml/Sass options are now taken from the page *rep* instead of the page ## 2.1.1 (2008-08-18) * Fixed issue which would cause files not to be required in the right order ## 2.1 (2008-08-17) This is only a short summary of all changes in 2.1. For details, see the [nanoc web site](http://nanoc.stoneship.org/). Especially the blog and the updated manual will be useful. New: * New `rdiscount` filter ([RDiscount site](http://github.com/rtomayko/rdiscount)) * New `maruku` filter ([Maruku site](https://github.com/bhollis/maruku/)) * New `erubis` filter ([Erubis site](http://www.kuwata-lab.com/erubis/)) * A better command-line frontend * A new filesystem data source named `filesystem_combined` * Routers, which decide where compiled pages should be written to * Page/layout mtimes can now be retrieved through `page.mtime`/`layout.mtime` Changed: * Already compiled pages will no longer be re-compiled unless outdated * Layout processors and filters have been merged * Layouts no longer rely on file extensions to determine the layout processor * Greatly improved source code documentation * Greatly improved unit test suite Removed: * Several filters have been removed and replaced by newer filters: * `eruby`: use `erb` or `erubis` instead * `markdown`: use `bluecloth`, `rdiscount` or `maruku` instead * `textile`: use `redcloth` instead ## 2.0.4 (2008-05-04) * Fixed `default.rb`’s `#html_escape` * Updated Haml filter and layout processor so that @page, @pages and @config are now available as instance variables instead of local variables ## 2.0.3 (2008-03-25) * The autocompiler now honors custom paths * The autocompiler now attempts to serve pages with the most appropriate MIME type, instead of always serving everything as `text/html` ## 2.0.2 (2008-01-26) * nanoc now requires Ruby 1.8.5 instead of 1.8.6 ## 2.0.1 (2008-01-21) * Fixed a “too many open files” error that could appear during (auto)compiling ## 2.0 (2007-12-25) New: * Support for custom layout processors * Support for custom data sources * Database data source * An auto-compiler * Pages have `parent` and `children` Changed: * The source has been restructured and cleaned up a great deal * Filters are defined in a different way now * The `eruby` filter now uses ERB instead of Erubis Removed: * The `filters` property; use `filters_pre` instead * Support for Liquid ## 1.6.2 (2007-10-23) * Fixed an issue which prevented the content capturing plugin from working ## 1.6.1 (2007-10-14) * Removed a stray debug message ## 1.6 (2007-10-13) * Added support for post-layout filters * Added support for getting a File object for the page, so you can now e.g. easily get the modification time for a given page (`@page.file.mtime`) * Cleaned up the source code a lot * Removed deprecated asset-copying functionality ## 1.5 (2007-09-10) * Added support for custom filters * Improved Liquid support -- Liquid is now a first-class nanoc citizen * Deprecated assets -- use something like rsync instead * Added `eruby_engine` option, which can be `erb` or `erubis` ## 1.4 (2007-07-06) * nanoc now supports ERB (as well as Erubis); Erubis no longer is a dependency * `meta.yaml` can now have `haml_options` property, which is passed to Haml * Pages can now have a `filename` property, which defaults to `index` [Dennis Sutch] * Pages now know in what order they should be compiled, eliminating the need for custom page ordering [Dennis Sutch] ## 1.3.1 (2007-06-30) * The contents of the `assets` directory are now copied into the output directory specified in `config.yaml` ## 1.3 (2007-06-24) * The `@pages` array now also contains uncompiled pages * Pages with `skip_output` set to true will not be outputted * Added new filters * Textile/RedCloth * Sass * nanoc now warns before overwriting in `create_site`, `create_page` and `create_template` (but not in compile) ## 1.2 (2007-06-05) * Sites now have an `assets` directory, whose contents are copied to the `output` directory when compiling [Soryu] * Added support for non-eRuby layouts (Markaby, Haml, Liquid, …) * Added more filters (Markaby, Haml, Liquid, RDoc [Dmitry Bilunov]) * Improved error reporting * Accessing page attributes using instance variables, and not through `@page`, is no longer possible * Page attributes can now be accessed using dot notation, i.e. `@page.title` as well as `@page[:title]` ## 1.1.3 (2007-05-18) * Fixed bug which would cause layoutless pages to be outputted incorrectly ## 1.1.2 (2007-05-17) * Backup files (files ending with a “~”) are now ignored * Fixed bug which would cause subpages not to be generated correctly ## 1.1 (2007-05-08) * Added support for nested layouts * Added coloured logging * `@page` now hold the page that is currently being processed * Index files are now called “content” files and are now named after the directory they are in [Colin Barrett] * It is now possible to access `@page` in the page’s content file ## 1.0.1 (2007-05-05) * Fixed a bug which would cause a “no such template” error to be displayed when the template existed but compiling it would raise an exception * Fixed bug which would cause pages not to be sorted by order before compiling ## 1.0 (2007-05-03) * Initial release nanoc-4.1.4/Gemfile.lock0000644000004100000410000001665212665031555015112 0ustar www-datawww-dataPATH remote: . specs: nanoc (4.1.4) cri (~> 2.3) GEM remote: https://rubygems.org/ specs: CFPropertyList (2.3.2) RedCloth (4.2.9) addressable (2.4.0) adsf (1.2.0) rack (>= 1.0.0) ast (2.2.0) bluecloth (2.2.0) builder (3.2.2) chunky_png (1.3.5) coderay (1.1.0) coffee-script (2.4.1) coffee-script-source execjs coffee-script-source (1.10.0) colored (1.2) commonjs (0.2.7) compass (1.0.3) chunky_png (~> 1.2) compass-core (~> 1.0.2) compass-import-once (~> 1.0.5) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) sass (>= 3.3.13, < 3.5) compass-core (1.0.3) multi_json (~> 1.0) sass (>= 3.3.0, < 3.5) compass-import-once (1.0.5) sass (>= 3.2, < 3.5) coveralls (0.8.10) json (~> 1.8) rest-client (>= 1.6.8, < 2) simplecov (~> 0.11.0) term-ansicolor (~> 1.3) thor (~> 0.19.1) tins (~> 1.6.0) crack (0.4.3) safe_yaml (~> 1.0.0) cri (2.7.0) colored (~> 1.2) diff-lcs (1.2.5) docile (1.1.5) domain_name (0.5.20160128) unf (>= 0.0.5, < 1.0.0) erubis (2.7.0) excon (0.45.4) execjs (2.6.0) ffi (1.9.10) fission (0.5.0) CFPropertyList (~> 2.2) fog (1.37.0) fog-aliyun (>= 0.1.0) fog-atmos fog-aws (>= 0.6.0) fog-brightbox (~> 0.4) fog-core (~> 1.32) fog-dynect (~> 0.0.2) fog-ecloud (~> 0.1) fog-google (<= 0.1.0) fog-json fog-local fog-powerdns (>= 0.1.1) fog-profitbricks fog-radosgw (>= 0.0.2) fog-riakcs fog-sakuracloud (>= 0.0.4) fog-serverlove fog-softlayer fog-storm_on_demand fog-terremark fog-vmfusion fog-voxel fog-vsphere (>= 0.4.0) fog-xenserver fog-xml (~> 0.1.1) ipaddress (~> 0.5) fog-aliyun (0.1.0) fog-core (~> 1.27) fog-json (~> 1.0) ipaddress (~> 0.8) xml-simple (~> 1.1) fog-atmos (0.1.0) fog-core fog-xml fog-aws (0.8.1) fog-core (~> 1.27) fog-json (~> 1.0) fog-xml (~> 0.1) ipaddress (~> 0.8) fog-brightbox (0.10.1) fog-core (~> 1.22) fog-json inflecto (~> 0.0.2) fog-core (1.35.0) builder excon (~> 0.45) formatador (~> 0.2) fog-dynect (0.0.2) fog-core fog-json fog-xml fog-ecloud (0.3.0) fog-core fog-xml fog-google (0.1.0) fog-core fog-json fog-xml fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) fog-local (0.2.1) fog-core (~> 1.27) fog-powerdns (0.1.1) fog-core (~> 1.27) fog-json (~> 1.0) fog-xml (~> 0.1) fog-profitbricks (0.0.5) fog-core fog-xml nokogiri fog-radosgw (0.0.5) fog-core (>= 1.21.0) fog-json fog-xml (>= 0.0.1) fog-riakcs (0.1.0) fog-core fog-json fog-xml fog-sakuracloud (1.7.5) fog-core fog-json fog-serverlove (0.1.2) fog-core fog-json fog-softlayer (1.0.3) fog-core fog-json fog-storm_on_demand (0.1.1) fog-core fog-json fog-terremark (0.1.0) fog-core fog-xml fog-vmfusion (0.1.0) fission fog-core fog-voxel (0.1.0) fog-core fog-xml fog-vsphere (0.6.0) fog-core rbvmomi (~> 1.8) fog-xenserver (0.2.3) fog-core fog-xml fog-xml (0.1.2) fog-core nokogiri (~> 1.5, >= 1.5.11) formatador (0.2.5) guard (2.13.0) formatador (>= 0.2.4) listen (>= 2.7, <= 4.0) lumberjack (~> 1.0) nenv (~> 0.1) notiffany (~> 0.0) pry (>= 0.9.12) shellany (~> 0.0) thor (>= 0.18.1) guard-rake (1.0.0) guard rake haml (4.0.7) tilt handlebars (0.7.0) handlebars-source (~> 3.0.0) therubyracer (~> 0.12.1) handlebars-source (3.0.3) hashdiff (0.3.0) http-cookie (1.0.2) domain_name (~> 0.5) inflecto (0.0.2) ipaddress (0.8.2) json (1.8.3) kramdown (1.9.0) less (2.6.0) commonjs (~> 0.2.7) libv8 (3.16.14.13) listen (3.0.6) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9.7) lumberjack (1.0.10) markaby (0.8.0) builder maruku (0.7.2) metaclass (0.0.4) method_source (0.8.2) mime-types (2.99) mini_portile2 (2.0.0) minitest (5.8.4) mocha (1.1.0) metaclass (~> 0.0.1) multi_json (1.11.2) mustache (1.0.2) nenv (0.3.0) netrc (0.11.0) nokogiri (1.6.7.2) mini_portile2 (~> 2.0.0.rc2) notiffany (0.0.8) nenv (~> 0.1) shellany (~> 0.0) pandoc-ruby (1.0.0) parser (2.3.0.5) ast (~> 2.2) posix-spawn (0.3.11) powerpack (0.1.1) pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) pygments.rb (0.6.3) posix-spawn (~> 0.3.6) yajl-ruby (~> 1.2.0) rack (1.6.4) rainbow (2.1.0) rainpress (1.0) rake (10.5.0) rb-fsevent (0.9.7) rb-inotify (0.9.7) ffi (>= 0.5.0) rbvmomi (1.8.2) builder nokogiri (>= 1.4.1) trollop rdiscount (2.1.8) rdoc (4.2.2) json (~> 1.4) redcarpet (3.3.4) ref (2.0.0) rest-client (1.8.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) rouge (1.10.1) rspec (3.4.0) rspec-core (~> 3.4.0) rspec-expectations (~> 3.4.0) rspec-mocks (~> 3.4.0) rspec-core (3.4.2) rspec-support (~> 3.4.0) rspec-expectations (3.4.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) rspec-mocks (3.4.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) rspec-support (3.4.1) rubocop (0.37.2) parser (>= 2.3.0.4, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 0.3) ruby-progressbar (1.7.5) rubypants (0.2.0) safe_yaml (1.0.4) sass (3.4.21) shellany (0.0.1) simplecov (0.11.2) docile (~> 1.1.0) json (~> 1.8) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) slim (3.0.6) temple (~> 0.7.3) tilt (>= 1.3.3, < 2.1) slop (3.6.0) temple (0.7.6) term-ansicolor (1.3.2) tins (~> 1.0) therubyracer (0.12.2) libv8 (~> 3.16.14.0) ref thor (0.19.1) tilt (2.0.2) tins (1.6.0) trollop (2.1.2) typogruby (1.0.18) rubypants uglifier (2.7.2) execjs (>= 0.3.0) json (>= 1.8.0) unf (0.1.4) unf_ext unf_ext (0.0.7.2) unicode-display_width (0.3.1) vcr (3.0.1) w3c_validators (1.2) json nokogiri webmock (1.22.6) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff xml-simple (1.1.5) yajl-ruby (1.2.1) yard (0.8.7.6) yuicompressor (1.3.3) PLATFORMS ruby DEPENDENCIES RedCloth adsf bluecloth builder bundler (>= 1.7.10, < 2.0) coderay coffee-script compass coveralls erubis fog guard-rake haml handlebars kramdown less (~> 2.0) listen markaby maruku mime-types minitest (~> 5.0) mocha mustache (~> 1.0) nanoc! nokogiri (~> 1.6) pandoc-ruby pry pygments.rb rack rainpress rake rdiscount rdoc redcarpet rouge rspec rubocop rubypants sass simplecov slim typogruby uglifier vcr w3c_validators webmock yard yuicompressor BUNDLED WITH 1.11.2 nanoc-4.1.4/nanoc.gemspec0000644000004100000410000000216112665031555015321 0ustar www-datawww-datarequire_relative 'lib/nanoc/version' Gem::Specification.new do |s| s.name = 'nanoc' s.version = Nanoc::VERSION s.homepage = 'http://nanoc.ws/' s.summary = 'A static-site generator with a focus on flexibility.' s.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.' s.author = 'Denis Defreyne' s.email = 'denis.defreyne@stoneship.org' s.license = 'MIT' s.files = Dir['[A-Z]*'] + Dir['doc/yardoc_{templates,handlers}/**/*'] + Dir['{bin,lib,tasks,test}/**/*'] + ['nanoc.gemspec'] s.executables = ['nanoc'] s.require_paths = ['lib'] s.rdoc_options = ['--main', 'README.md'] s.extra_rdoc_files = ['ChangeLog', 'LICENSE', 'README.md', 'NEWS.md'] s.required_ruby_version = '>= 2.1.0' s.add_runtime_dependency('cri', '~> 2.3') s.add_development_dependency('bundler', '>= 1.7.10', '< 2.0') end nanoc-4.1.4/bin/0000755000004100000410000000000012665031555013426 5ustar www-datawww-datananoc-4.1.4/bin/nanoc0000755000004100000410000000042612665031555014454 0ustar www-datawww-data#!/usr/bin/env ruby require 'nanoc' require 'nanoc/cli' if File.file?('Gemfile') && !defined?(Bundler) warn 'A Gemfile was detected, but Bundler is not loaded. This is probably not what you want. To run Nanoc with Bundler, use `bundle exec nanoc`.' end Nanoc::CLI.run(ARGV) nanoc-4.1.4/Gemfile0000644000004100000410000000201712665031555014151 0ustar www-datawww-datasource 'https://rubygems.org' gemspec group :devel do gem 'coveralls', require: false gem 'guard-rake' gem 'minitest', '~> 5.0' gem 'mocha' gem 'pry' gem 'rake' gem 'rdoc' gem 'rspec' gem 'rubocop' gem 'simplecov', require: false gem 'vcr' gem 'webmock' gem 'yard' end group :plugins do gem 'adsf' gem 'bluecloth', platforms: :ruby gem 'builder' gem 'coderay' gem 'coffee-script' gem 'compass' gem 'erubis' gem 'fog' gem 'haml' gem 'handlebars', platforms: :ruby gem 'kramdown' gem 'less', '~> 2.0', platforms: :ruby gem 'listen' gem 'markaby' gem 'maruku' gem 'mime-types' gem 'mustache', '~> 1.0' gem 'nokogiri', '~> 1.6' gem 'pandoc-ruby' gem 'pygments.rb', platforms: [:ruby, :mswin] gem 'rack' gem 'rainpress' gem 'rdiscount', platforms: [:ruby, :mswin] gem 'redcarpet', platforms: [:ruby, :mswin] gem 'RedCloth' gem 'rouge' gem 'rubypants' gem 'sass' gem 'slim' gem 'typogruby' gem 'uglifier' gem 'w3c_validators' gem 'yuicompressor' end nanoc-4.1.4/doc/0000755000004100000410000000000012665031555013423 5ustar www-datawww-datananoc-4.1.4/doc/yardoc_handlers/0000755000004100000410000000000012665031555016564 5ustar www-datawww-datananoc-4.1.4/doc/yardoc_handlers/identifier.rb0000644000004100000410000000173012665031555021234 0ustar www-datawww-dataclass NanocIdentifierHandler < ::YARD::Handlers::Ruby::AttributeHandler # e.g. identifier :foo, :bar handles method_call(:identifier), method_call(:identifiers) namespace_only def process identifiers = statement.parameters(false).map { |param| param.jump(:ident)[0] } namespace['nanoc_identifiers'] = identifiers end end class NanocRegisterFilterHandler < ::YARD::Handlers::Ruby::AttributeHandler # e.g. Nanoc::Filter.register '::Nanoc::Filters::AsciiDoc', :asciidoc handles method_call(:register) namespace_only def process target = statement.jump(:const_path_ref) return if target != s(:const_path_ref, s(:var_ref, s(:const, 'Nanoc')), s(:const, 'Filter')) class_name = statement.jump(:string_literal).jump(:tstring_content)[0] identifier = statement.jump(:symbol_literal).jump(:ident)[0] obj = YARD::Registry.at(class_name.sub(/^::/, '')) obj['nanoc_identifiers'] ||= [] obj['nanoc_identifiers'] << identifier end end nanoc-4.1.4/doc/yardoc_templates/0000755000004100000410000000000012665031555016762 5ustar www-datawww-datananoc-4.1.4/doc/yardoc_templates/default/0000755000004100000410000000000012665031555020406 5ustar www-datawww-datananoc-4.1.4/doc/yardoc_templates/default/layout/0000755000004100000410000000000012665031555021723 5ustar www-datawww-datananoc-4.1.4/doc/yardoc_templates/default/layout/html/0000755000004100000410000000000012665031555022667 5ustar www-datawww-datananoc-4.1.4/doc/yardoc_templates/default/layout/html/footer.erb0000644000004100000410000000136612665031555024665 0ustar www-datawww-data<%= superb %> nanoc-4.1.4/lib/0000755000004100000410000000000012665031555013424 5ustar www-datawww-datananoc-4.1.4/lib/nanoc.rb0000644000004100000410000000225412665031555015052 0ustar www-datawww-datamodule Nanoc # @return [String] A string containing information about this Nanoc version # and its environment (Ruby engine and version, Rubygems version if any). # # @api private def self.version_information gem_info = defined?(Gem) ? "with RubyGems #{Gem::VERSION}" : 'without RubyGems' engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' res = '' res << "Nanoc #{Nanoc::VERSION} © 2007-2016 Denis Defreyne.\n" res << "Running #{engine} #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) on #{RUBY_PLATFORM} #{gem_info}.\n" res end # @return [Boolean] True if the current platform is Windows, false otherwise. # # @api private def self.on_windows? RUBY_PLATFORM =~ /windows|bccwin|cygwin|djgpp|mingw|mswin|wince/i end end # Load general requirements require 'digest' require 'enumerator' require 'fileutils' require 'forwardable' require 'pathname' require 'pstore' require 'set' require 'tempfile' require 'thread' require 'time' require 'yaml' require 'English' # Load Nanoc require 'nanoc/version' require 'nanoc/base' require 'nanoc/extra' require 'nanoc/data_sources' require 'nanoc/filters' require 'nanoc/helpers' require 'nanoc/rule_dsl' nanoc-4.1.4/lib/nanoc/0000755000004100000410000000000012665031555014522 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/0000755000004100000410000000000012665031555015434 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/core_ext.rb0000644000004100000410000000022312665031555017566 0ustar www-datawww-datarequire 'nanoc/base/core_ext/array' require 'nanoc/base/core_ext/hash' require 'nanoc/base/core_ext/pathname' require 'nanoc/base/core_ext/string' nanoc-4.1.4/lib/nanoc/base/views/0000755000004100000410000000000012665031555016571 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/views/item_rep_view.rb0000644000004100000410000000440212665031555021754 0ustar www-datawww-datamodule Nanoc class ItemRepView < ::Nanoc::View # @api private def initialize(item_rep, context) super(context) @item_rep = item_rep end # @api private def unwrap @item_rep end # @see Object#== def ==(other) other.respond_to?(:item) && other.respond_to?(:name) && item == other.item && name == other.name end alias eql? == # @see Object#hash def hash self.class.hash ^ item.identifier.hash ^ name.hash end # @return [Symbol] def name @item_rep.name end # Returns the compiled content. # # @param [String] snapshot The name of the snapshot from which to # fetch the compiled content. By default, the returned compiled content # will be the content compiled right before the first layout call (if # any). # # @return [String] The content at the given snapshot. def compiled_content(snapshot: nil) Nanoc::Int::NotificationCenter.post(:visit_started, unwrap.item) Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap.item) @item_rep.compiled_content(snapshot: snapshot) end # Returns the item rep’s path, as used when being linked to. It starts # with a slash and it is relative to the output directory. It does not # include the path to the output directory. It will not include the # filename if the filename is an index filename. # # @param [Symbol] snapshot The snapshot for which the path should be # returned. # # @return [String] The item rep’s path. def path(snapshot: :last) Nanoc::Int::NotificationCenter.post(:visit_started, unwrap.item) Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap.item) @item_rep.path(snapshot: snapshot) end # Returns the item that this item rep belongs to. # # @return [Nanoc::ItemWithRepsView] def item Nanoc::ItemWithRepsView.new(@item_rep.item, @context) end # @api private def raw_path(snapshot: :last) Nanoc::Int::NotificationCenter.post(:visit_started, unwrap.item) Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap.item) @item_rep.raw_path(snapshot: snapshot) end # @api private def binary? @item_rep.binary? end end end nanoc-4.1.4/lib/nanoc/base/views/config_view.rb0000644000004100000410000000141612665031555021417 0ustar www-datawww-datamodule Nanoc class ConfigView < ::Nanoc::View # @api private NONE = Object.new.freeze # @api private def initialize(config, context) super(context) @config = config end # @api private def unwrap @config end # @see Hash#fetch def fetch(key, fallback = NONE, &_block) @config.fetch(key) do if !fallback.equal?(NONE) fallback elsif block_given? yield(key) else raise KeyError, "key not found: #{key.inspect}" end end end # @see Hash#key? def key?(key) @config.key?(key) end # @see Hash#[] def [](key) @config[key] end # @see Hash#each def each(&block) @config.each(&block) end end end nanoc-4.1.4/lib/nanoc/base/views/mixins/0000755000004100000410000000000012665031555020100 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/views/mixins/document_view_mixin.rb0000644000004100000410000000341112665031555024500 0ustar www-datawww-datamodule Nanoc module DocumentViewMixin # @api private NONE = Object.new.freeze # @api private def initialize(document, context) super(context) @document = document end # @api private def unwrap @document end # @see Object#== def ==(other) other.respond_to?(:identifier) && identifier == other.identifier end alias eql? == # @see Object#hash def hash self.class.hash ^ identifier.hash end # @return [Nanoc::Identifier] def identifier unwrap.identifier end # @see Hash#[] def [](key) Nanoc::Int::NotificationCenter.post(:visit_started, unwrap) Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap) unwrap.attributes[key] end # @return [Hash] def attributes Nanoc::Int::NotificationCenter.post(:visit_started, unwrap) Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap) unwrap.attributes end # @see Hash#fetch def fetch(key, fallback = NONE, &_block) Nanoc::Int::NotificationCenter.post(:visit_started, unwrap) Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap) if unwrap.attributes.key?(key) unwrap.attributes[key] elsif !fallback.equal?(NONE) fallback elsif block_given? yield(key) else raise KeyError, "key not found: #{key.inspect}" end end # @see Hash#key? def key?(key) Nanoc::Int::NotificationCenter.post(:visit_started, unwrap) Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap) unwrap.attributes.key?(key) end # @api private def reference unwrap.reference end # @api private def raw_content unwrap.content.string end end end nanoc-4.1.4/lib/nanoc/base/views/mixins/mutable_document_view_mixin.rb0000644000004100000410000000230212665031555026207 0ustar www-datawww-datamodule Nanoc module MutableDocumentViewMixin # @api private class DisallowedAttributeValueError < Nanoc::Error attr_reader :value def initialize(value) @value = value end def message "The #{value.class} cannot be stored inside an attribute. Store its identifier instead." end end # Sets the value for the given attribute. # # @param [Symbol] key # # @see Hash#[]= def []=(key, value) disallowed_value_classes = Set.new([ Nanoc::Int::Item, Nanoc::Int::Layout, Nanoc::ItemWithRepsView, Nanoc::LayoutView, ]) if disallowed_value_classes.include?(value.class) raise DisallowedAttributeValueError.new(value) end unwrap.attributes[key] = value end # Sets the identifier to the given argument. # # @param [String, Nanoc::Identifier] arg def identifier=(arg) unwrap.identifier = Nanoc::Identifier.from(arg) end # Updates the attributes based on the given hash. # # @param [Hash] hash # # @return [self] def update_attributes(hash) hash.each { |k, v| unwrap.attributes[k] = v } self end end end nanoc-4.1.4/lib/nanoc/base/views/mixins/with_reps_view_mixin.rb0000644000004100000410000000315212665031555024670 0ustar www-datawww-datamodule Nanoc module WithRepsViewMixin # Returns the compiled content. # # @param [String] rep The name of the representation # from which the compiled content should be fetched. By default, the # compiled content will be fetched from the default representation. # # @param [String] snapshot The name of the snapshot from which to # fetch the compiled content. By default, the returned compiled content # will be the content compiled right before the first layout call (if # any). # # @return [String] The content of the given rep at the given snapshot. def compiled_content(rep: :default, snapshot: :pre) reps.fetch(rep).compiled_content(snapshot: snapshot) end # Returns the item path, as used when being linked to. It starts # with a slash and it is relative to the output directory. It does not # include the path to the output directory. It will not include the # filename if the filename is an index filename. # # @param [String] rep The name of the representation # from which the path should be fetched. By default, the path will be # fetched from the default representation. # # @param [Symbol] snapshot The snapshot for which the # path should be returned. # # @return [String] The item’s path. def path(rep: :default, snapshot: :last) reps.fetch(rep).path(snapshot: snapshot) end # Returns the representations of this item. # # @return [Nanoc::ItemRepCollectionView] def reps Nanoc::ItemRepCollectionView.new(@context.reps[unwrap], @context) end end end nanoc-4.1.4/lib/nanoc/base/views/item_with_reps_view.rb0000644000004100000410000000016412665031555023173 0ustar www-datawww-datamodule Nanoc class ItemWithRepsView < ::Nanoc::ItemWithoutRepsView include Nanoc::WithRepsViewMixin end end nanoc-4.1.4/lib/nanoc/base/views/layout_collection_view.rb0000644000004100000410000000024012665031555023674 0ustar www-datawww-datamodule Nanoc class LayoutCollectionView < ::Nanoc::IdentifiableCollectionView # @api private def view_class Nanoc::LayoutView end end end nanoc-4.1.4/lib/nanoc/base/views/mutable_config_view.rb0000644000004100000410000000035012665031555023124 0ustar www-datawww-datamodule Nanoc class MutableConfigView < Nanoc::ConfigView # Sets the value for the given attribute. # # @param [Symbol] key # # @see Hash#[]= def []=(key, value) @config[key] = value end end end nanoc-4.1.4/lib/nanoc/base/views/mutable_layout_collection_view.rb0000644000004100000410000000120712665031555025411 0ustar www-datawww-datamodule Nanoc class MutableLayoutCollectionView < Nanoc::MutableIdentifiableCollectionView # @api private def view_class Nanoc::MutableLayoutView end # Creates a new layout and adds it to the site’s collection of layouts. # # @param [String] content The layout content. # # @param [Hash] attributes A hash containing this layout's attributes. # # @param [Nanoc::Identifier, String] identifier This layout's identifier. # # @return [self] def create(content, attributes, identifier) @objects << Nanoc::Int::Layout.new(content, attributes, identifier) self end end end nanoc-4.1.4/lib/nanoc/base/views/layout_view.rb0000644000004100000410000000013712665031555021466 0ustar www-datawww-datamodule Nanoc class LayoutView < ::Nanoc::View include Nanoc::DocumentViewMixin end end nanoc-4.1.4/lib/nanoc/base/views/mutable_item_view.rb0000644000004100000410000000017012665031555022615 0ustar www-datawww-datamodule Nanoc class MutableItemView < Nanoc::ItemWithoutRepsView include Nanoc::MutableDocumentViewMixin end end nanoc-4.1.4/lib/nanoc/base/views/post_compile_item_view.rb0000644000004100000410000000022612665031555023663 0ustar www-datawww-datamodule Nanoc class PostCompileItemView < Nanoc::ItemWithRepsView def modified reps.select { |rep| rep.unwrap.modified } end end end nanoc-4.1.4/lib/nanoc/base/views/item_collection_with_reps_view.rb0000644000004100000410000000025412665031555025406 0ustar www-datawww-datamodule Nanoc class ItemCollectionWithRepsView < ::Nanoc::IdentifiableCollectionView # @api private def view_class Nanoc::ItemWithRepsView end end end nanoc-4.1.4/lib/nanoc/base/views/item_without_reps_view.rb0000644000004100000410000000301712665031555023723 0ustar www-datawww-datamodule Nanoc class ItemWithoutRepsView < ::Nanoc::View include Nanoc::DocumentViewMixin # Returns the children of this item. For items with identifiers that have # extensions, returns an empty collection. # # @return [Enumerable] def children unless unwrap.identifier.legacy? raise Nanoc::Int::Errors::CannotGetParentOrChildrenOfNonLegacyItem.new(unwrap.identifier) end children_pattern = Nanoc::Int::Pattern.from(unwrap.identifier.to_s + '*/') children = @context.items.select { |i| children_pattern.match?(i.identifier) } children.map { |i| self.class.new(i, @context) }.freeze end # Returns the parent of this item, if one exists. For items with identifiers # that have extensions, returns nil. # # @return [Nanoc::ItemWithRepsView] if the item has a parent # # @return [nil] if the item has no parent def parent unless unwrap.identifier.legacy? raise Nanoc::Int::Errors::CannotGetParentOrChildrenOfNonLegacyItem.new(unwrap.identifier) end parent_identifier = '/' + unwrap.identifier.components[0..-2].join('/') + '/' parent_identifier = '/' if parent_identifier == '//' parent = @context.items[parent_identifier] parent && self.class.new(parent, @context) end # @return [Boolean] True if the item is binary, false otherwise def binary? unwrap.content.binary? end # @api private def raw_filename unwrap.content.filename end end end nanoc-4.1.4/lib/nanoc/base/views/mutable_layout_view.rb0000644000004100000410000000016112665031555023174 0ustar www-datawww-datamodule Nanoc class MutableLayoutView < Nanoc::LayoutView include Nanoc::MutableDocumentViewMixin end end nanoc-4.1.4/lib/nanoc/base/views/view.rb0000644000004100000410000000062112665031555020067 0ustar www-datawww-datamodule Nanoc class View # @api private def initialize(context) @context = context end def _context @context end # @api private def unwrap raise NotImplementedError end # True if the wrapped object is frozen; false otherwise. # # @return [Boolean] # # @see Object#frozen? def frozen? unwrap.frozen? end end end nanoc-4.1.4/lib/nanoc/base/views/identifiable_collection_view.rb0000644000004100000410000000333512665031555025006 0ustar www-datawww-datamodule Nanoc class IdentifiableCollectionView < ::Nanoc::View include Enumerable # @api private def initialize(objects, context) super(context) @objects = objects end # @api private def unwrap @objects end # @abstract # # @api private def view_class raise NotImplementedError end # Calls the given block once for each object, passing that object as a parameter. # # @yieldparam [#identifier] object # # @yieldreturn [void] # # @return [self] def each @objects.each { |i| yield view_class.new(i, @context) } self end # @return [Integer] def size @objects.size end # Finds all objects whose identifier matches the given argument. # # @param [String, Regex] arg # # @return [Enumerable] def find_all(arg) pat = Nanoc::Int::Pattern.from(arg) select { |i| pat.match?(i.identifier) } end # @overload [](string) # # Finds the object whose identifier matches the given string. # # If the glob syntax is enabled, the string can be a glob, in which case # this method finds the first object that matches the given glob. # # @param [String] string # # @return [nil] if no object matches the string # # @return [#identifier] if an object was found # # @overload [](regex) # # Finds the object whose identifier matches the given regular expression. # # @param [Regex] regex # # @return [nil] if no object matches the regex # # @return [#identifier] if an object was found def [](arg) res = @objects[arg] res && view_class.new(res, @context) end end end nanoc-4.1.4/lib/nanoc/base/views/site_view.rb0000644000004100000410000000032412665031555021113 0ustar www-datawww-datamodule Nanoc class SiteView < ::Nanoc::View # @api private def initialize(site, context) super(context) @site = site end # @api private def unwrap @site end end end nanoc-4.1.4/lib/nanoc/base/views/post_compile_item_collection_view.rb0000644000004100000410000000026012665031555026074 0ustar www-datawww-datamodule Nanoc class PostCompileItemCollectionView < Nanoc::IdentifiableCollectionView # @api private def view_class Nanoc::PostCompileItemView end end end nanoc-4.1.4/lib/nanoc/base/views/view_context.rb0000644000004100000410000000027412665031555021637 0ustar www-datawww-datamodule Nanoc # @api private class ViewContext attr_reader :reps attr_reader :items def initialize(reps:, items:) @reps = reps @items = items end end end nanoc-4.1.4/lib/nanoc/base/views/mutable_item_collection_view.rb0000644000004100000410000000203512665031555025032 0ustar www-datawww-datamodule Nanoc class MutableItemCollectionView < Nanoc::MutableIdentifiableCollectionView # @api private def view_class Nanoc::MutableItemView end # Creates a new item and adds it to the site’s collection of items. # # @param [String] content The uncompiled item content (if it is a textual # item) or the path to the filename containing the content (if it is a # binary item). # # @param [Hash] attributes A hash containing this item's attributes. # # @param [Nanoc::Identifier, String] identifier This item's identifier. # # @param [Boolean] binary Whether or not this item is binary # # @param [String] filename Absolute path to the file # containing this content (if any) # # @return [self] def create(content, attributes, identifier, binary: false, filename: nil) content = Nanoc::Int::Content.create(content, binary: binary, filename: filename) @objects << Nanoc::Int::Item.new(content, attributes, identifier) self end end end nanoc-4.1.4/lib/nanoc/base/views/item_collection_without_reps_view.rb0000644000004100000410000000026212665031555026135 0ustar www-datawww-datamodule Nanoc class ItemCollectionWithoutRepsView < ::Nanoc::IdentifiableCollectionView # @api private def view_class Nanoc::ItemWithoutRepsView end end end nanoc-4.1.4/lib/nanoc/base/views/mutable_identifiable_collection_view.rb0000644000004100000410000000060412665031555026513 0ustar www-datawww-datamodule Nanoc class MutableIdentifiableCollectionView < Nanoc::IdentifiableCollectionView # Deletes every object for which the block evaluates to true. # # @yieldparam [#identifier] object # # @yieldreturn [Boolean] # # @return [self] def delete_if(&_block) @objects.delete_if { |o| yield(view_class.new(o, @context)) } self end end end nanoc-4.1.4/lib/nanoc/base/views/item_rep_collection_view.rb0000644000004100000410000000405012665031555024166 0ustar www-datawww-datamodule Nanoc class ItemRepCollectionView < ::Nanoc::View include Enumerable class NoSuchItemRepError < ::Nanoc::Error def initialize(rep_name) super("No rep named #{rep_name.inspect} was found.") end end # @api private def initialize(item_reps, context) super(context) @item_reps = item_reps end # @api private def unwrap @item_reps end def to_ary @item_reps.map { |ir| Nanoc::ItemRepView.new(ir, @context) } end # Calls the given block once for each item rep, passing that item rep as a parameter. # # @yieldparam [Nanoc::ItemRepView] item rep # # @yieldreturn [void] # # @return [self] def each @item_reps.each { |ir| yield Nanoc::ItemRepView.new(ir, @context) } self end # @return [Integer] def size @item_reps.size end # Return the item rep with the given name, or nil if no item rep exists. # # @param [Symbol] rep_name # # @return [nil] if no item rep with the given name was found # # @return [Nanoc::ItemRepView] if an item rep with the given name was found def [](rep_name) case rep_name when Symbol res = @item_reps.find { |ir| ir.name == rep_name } res && Nanoc::ItemRepView.new(res, @context) when Fixnum raise ArgumentError, "expected ItemRepCollectionView#[] to be called with a symbol (you likely want `.reps[:default]` rather than `.reps[#{rep_name}]`)" else raise ArgumentError, 'expected ItemRepCollectionView#[] to be called with a symbol' end end # Return the item rep with the given name, or raises an exception if there # is no rep with the given name. # # @param [Symbol] rep_name # # @return [Nanoc::ItemRepView] # # @raise if no rep was found def fetch(rep_name) res = @item_reps.find { |ir| ir.name == rep_name } if res Nanoc::ItemRepView.new(res, @context) else raise NoSuchItemRepError.new(rep_name) end end end end nanoc-4.1.4/lib/nanoc/base/core_ext/0000755000004100000410000000000012665031555017244 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/core_ext/string.rb0000644000004100000410000000046712665031555021106 0ustar www-datawww-data# @api private module Nanoc::StringExtensions # Transforms string into an actual identifier # # @return [String] The identifier generated from the receiver def __nanoc_cleaned_identifier "/#{self}/".gsub(/^\/+|\/+$/, '/') end end # @api private class String include Nanoc::StringExtensions end nanoc-4.1.4/lib/nanoc/base/core_ext/hash.rb0000644000004100000410000000232612665031555020517 0ustar www-datawww-data# @api private module Nanoc::HashExtensions # Returns a new hash where all keys are recursively converted to symbols by # calling {Nanoc::ArrayExtensions#__nanoc_symbolize_keys_recursively} or # {Nanoc::HashExtensions#__nanoc_symbolize_keys_recursively}. # # @return [Hash] The converted hash def __nanoc_symbolize_keys_recursively hash = {} each_pair do |key, value| new_key = key.respond_to?(:to_sym) ? key.to_sym : key new_value = value.respond_to?(:__nanoc_symbolize_keys_recursively) ? value.__nanoc_symbolize_keys_recursively : value hash[new_key] = new_value end hash end # Freezes the contents of the hash, as well as all hash values. The hash # values will be frozen using {#__nanoc_freeze_recursively} if they respond to # that message, or #freeze if they do not. # # @see Array#__nanoc_freeze_recursively # # @return [void] # # @since 3.2.0 def __nanoc_freeze_recursively return if frozen? freeze each_pair do |_key, value| if value.respond_to?(:__nanoc_freeze_recursively) value.__nanoc_freeze_recursively else value.freeze end end end end # @api private class Hash include Nanoc::HashExtensions end nanoc-4.1.4/lib/nanoc/base/core_ext/array.rb0000644000004100000410000000217712665031555020716 0ustar www-datawww-data# @api private module Nanoc::ArrayExtensions # Returns a new array where all items' keys are recursively converted to # symbols by calling {Nanoc::ArrayExtensions#__nanoc_symbolize_keys_recursively} or # {Nanoc::HashExtensions#__nanoc_symbolize_keys_recursively}. # # @return [Array] The converted array def __nanoc_symbolize_keys_recursively array = [] each do |element| array << (element.respond_to?(:__nanoc_symbolize_keys_recursively) ? element.__nanoc_symbolize_keys_recursively : element) end array end # Freezes the contents of the array, as well as all array elements. The # array elements will be frozen using {#__nanoc_freeze_recursively} if they respond # to that message, or #freeze if they do not. # # @see Hash#__nanoc_freeze_recursively # # @return [void] # # @since 3.2.0 def __nanoc_freeze_recursively return if frozen? freeze each do |value| if value.respond_to?(:__nanoc_freeze_recursively) value.__nanoc_freeze_recursively else value.freeze end end end end # @api private class Array include Nanoc::ArrayExtensions end nanoc-4.1.4/lib/nanoc/base/core_ext/pathname.rb0000644000004100000410000000017312665031555021367 0ustar www-datawww-data# @api private module Nanoc::PathnameExtensions end # @api private class Pathname include Nanoc::PathnameExtensions end nanoc-4.1.4/lib/nanoc/base/plugin_registry.rb0000644000004100000410000001527112665031555021215 0ustar www-datawww-datamodule Nanoc::Int # The class responsible for keeping track of all loaded plugins, such as # filters ({Nanoc::Filter}) and data sources ({Nanoc::DataSource}). # # @api private class PluginRegistry extend Nanoc::Int::Memoization # A module that contains class methods for plugins. It provides functions # for setting identifiers, registering plugins and finding plugins. Plugin # classes should extend this module. module PluginMethods # @overload identifiers(*identifiers) # # Sets the identifiers for this plugin. # # @param [Array] identifiers A list of identifiers to assign to # this plugin. # # @return [void] # # @overload identifiers # # @return [Array] The identifiers for this plugin def identifiers(*identifiers) if identifiers.empty? Nanoc::Int::PluginRegistry.instance.identifiers_of(superclass, self) else register(self, *identifiers) end end # @overload identifier(identifier) # # Sets the identifier for this plugin. # # @param [Symbol] identifier An identifier to assign to this plugin. # # @return [void] # # @overload identifier # # @return [Symbol] The first identifier for this plugin def identifier(identifier = nil) if identifier identifiers(identifier) else Nanoc::Int::PluginRegistry.instance.identifiers_of(superclass, self).first end end # Registers the given class as a plugin with the given identifier. # # @param [Class, String] class_or_name The class to register, or a # string containing the class name to register. # # @param [Array] identifiers A list of identifiers to assign to # this plugin. # # @return [void] def register(class_or_name, *identifiers) # Find plugin class klass = self klass = klass.superclass while klass.superclass.respond_to?(:register) # Register registry = Nanoc::Int::PluginRegistry.instance registry.register(klass, class_or_name, *identifiers) end # @return [Hash] All plugins of this type, with keys # being the identifiers and values the plugin classes def all Nanoc::Int::PluginRegistry.instance.find_all(self) end # Returns the plugin with the given name (identifier) # # @param [String] name The name of the plugin class to find # # @return [Class] The plugin class with the given name def named(name) Nanoc::Int::PluginRegistry.instance.find(self, name) end end # Returns the shared {PluginRegistry} instance, creating it if none exists # yet. # # @return [Nanoc::Int::PluginRegistry] The shared plugin registry def self.instance @instance ||= new end # Creates a new plugin registry. This should usually not be necessary; it # is recommended to use the shared instance (obtained from # {Nanoc::Int::PluginRegistry.instance}). def initialize @identifiers_to_classes = {} @classes_to_identifiers = {} end # Registers the given class as a plugin. # # @param [Class] superclass The superclass of the plugin. For example: # {Nanoc::Filter}. # # @param [Class, String] class_or_name The class to register. This can be # a string, in which case it will be automatically converted to a proper # class at lookup. For example: `Nanoc::Filters::ERB`, # `"Nanoc::Filters::Haml"`. # # @param [Symbol] identifiers One or more symbols identifying the class. # For example: `:haml`, :`erb`. # # @return [void] def register(superclass, class_or_name, *identifiers) @identifiers_to_classes[superclass] ||= {} @classes_to_identifiers[superclass] ||= {} identifiers.each do |identifier| @identifiers_to_classes[superclass][identifier.to_sym] = class_or_name (@classes_to_identifiers[superclass][name_for_class(class_or_name)] ||= []) << identifier.to_sym end end # @param [Class] superclass The superclass of the plugin. For example: # {Nanoc::Filter}. # # @param [Class] klass The class to get the identifiers for. # # @return [Array] An array of identifiers for the given class def identifiers_of(superclass, klass) (@classes_to_identifiers[superclass] || {})[name_for_class(klass)] || [] end # Finds the plugin that is a subclass of the given class and has the given # name. # # @param [Class] klass The class of the plugin to return # # @param [Symbol] name The name of the plugin to return # # @return [Class, nil] The plugin with the given name def find(klass, name) @identifiers_to_classes[klass] ||= {} resolve(@identifiers_to_classes[klass][name.to_sym], klass) end # Returns all plugins of the given class. # # @param [Class] klass The class of the plugin to return # # @return [Enumerable] A collection of class plugins def find_all(klass) @identifiers_to_classes[klass] ||= {} res = {} @identifiers_to_classes[klass].each_pair { |k, v| res[k] = resolve(v, k) } res end # Returns a list of all plugins. The returned list of plugins is an array # with array elements in the following format: # # { :class => ..., :superclass => ..., :identifiers => ... } # # @return [Array] A list of all plugins in the format described def all plugins = [] @identifiers_to_classes.each_pair do |superclass, submap| submap.each_pair do |identifier, klass| # Find existing plugin existing_plugin = plugins.find do |p| p[:class] == klass && p[:superclass] == superclass end if existing_plugin # Add identifier to existing plugin existing_plugin[:identifiers] << identifier existing_plugin[:identifiers] = existing_plugin[:identifiers].sort_by(&:to_s) else # Create new plugin plugins << { class: klass, superclass: superclass, identifiers: [identifier], } end end end plugins end protected def resolve(class_or_name, _klass) if class_or_name.is_a?(String) class_or_name.scan(/\w+/).reduce(Kernel) do |memo, part| memo.const_get(part) end else class_or_name end end memoize :resolve def name_for_class(klass) klass.to_s.sub(/^(::)?/, '::') end end end nanoc-4.1.4/lib/nanoc/base/repos.rb0000644000004100000410000000047612665031555017120 0ustar www-datawww-datarequire_relative 'repos/store' require_relative 'repos/checksum_store' require_relative 'repos/compiled_content_cache' require_relative 'repos/config_loader' require_relative 'repos/data_source' require_relative 'repos/dependency_store' require_relative 'repos/rule_memory_store' require_relative 'repos/site_loader' nanoc-4.1.4/lib/nanoc/base/errors.rb0000644000004100000410000002116512665031555017302 0ustar www-datawww-datamodule Nanoc::Int # Module that contains all Nanoc-specific errors. # # @api private module Errors Generic = ::Nanoc::Error # Generic trivial error. Superclass for all Nanoc-specific errors that are # considered "trivial", i.e. errors that do not require a full crash report. class GenericTrivial < Generic end # Error that is raised when a site is loaded that uses a data source with # an unknown identifier. class UnknownDataSource < Generic # @param [String] data_source_name The data source name for which no # data source could be found def initialize(data_source_name) super("The data source specified in the site’s configuration file, “#{data_source_name}”, does not exist.") end end # Error that is raised during site compilation when an item uses a layout # that is not present in the site. class UnknownLayout < Generic # @param [String] layout_identifier The layout identifier for which no # layout could be found def initialize(layout_identifier) super("The site does not have a layout with identifier “#{layout_identifier}”.") end end # Error that is raised during site compilation when an item uses a filter # that is not known. class UnknownFilter < Generic # @param [Symbol] filter_name The filter name for which no filter could # be found def initialize(filter_name) super("The requested filter, “#{filter_name}”, does not exist.") end end # Error that is raised during site compilation when a layout is compiled # for which the filter cannot be determined. This is similar to the # {UnknownFilter} error, but specific for filters for layouts. class CannotDetermineFilter < Generic # @param [String] layout_identifier The identifier of the layout for # which the filter could not be determined def initialize(layout_identifier) super("The filter to be used for the “#{layout_identifier}” layout could not be determined. Make sure the layout does have a filter.") end end # Error that is raised during site compilation when an item (directly or # indirectly) includes its own item content, leading to endless recursion. class RecursiveCompilation < Generic # @param [Array] reps A list of item representations # that mutually depend on each other def initialize(reps) list = reps.map(&:inspect).join("\n") super("The site cannot be compiled because the following items mutually depend on each other:\n#{list}.") end end # Error that is raised when no rules file can be found in the current # working directory. class NoRulesFileFound < Generic def initialize super('This site does not have a rules file, which is required for Nanoc sites.') end end # Error that is raised when no compilation rule that can be applied to the # current item can be found. class NoMatchingCompilationRuleFound < Generic # @param [Nanoc::Int::Item] item The item for which no compilation rule # could be found def initialize(item) super("No compilation rules were found for the “#{item.identifier}” item.") end end # Error that is raised when no routing rule that can be applied to the # current item can be found. class NoMatchingRoutingRuleFound < Generic # @param [Nanoc::Int::ItemRep] rep The item repiresentation for which no # routing rule could be found def initialize(rep) super("No routing rules were found for the “#{rep.item.identifier}” item (rep “#{rep.name}”).") end end # Error that is raised when an rep cannot be compiled because it depends # on other representations. class UnmetDependency < Generic # @return [Nanoc::Int::ItemRep] The item representation that cannot yet be # compiled attr_reader :rep # @param [Nanoc::Int::ItemRep] rep The item representation that cannot yet be # compiled def initialize(rep) @rep = rep super("The current item cannot be compiled yet because of an unmet dependency on the “#{rep.item.identifier}” item (rep “#{rep.name}”).") end end # Error that is raised when a binary item is attempted to be laid out. class CannotLayoutBinaryItem < Generic # @param [Nanoc::Int::ItemRep] rep The item representation that was attempted # to be laid out def initialize(rep) super("The “{rep.item.identifier}” item (rep “#{rep.name}”) cannot be laid out because it is a binary item. If you are getting this error for an item that should be textual instead of binary, make sure that its extension is included in the text_extensions array in the site configuration.") end end # Error that is raised when a textual filter is attempted to be applied to # a binary item representation. class CannotUseTextualFilter < Generic # @param [Nanoc::Int::ItemRep] rep The item representation that was # attempted to be filtered # # @param [Class] filter_class The filter class that was used def initialize(rep, filter_class) super("The “#{filter_class.inspect}” filter cannot be used to filter the “#{rep.item.identifier}” item (rep “#{rep.name}”), because textual filters cannot be used on binary items.") end end # Error that is raised when a binary filter is attempted to be applied to # a textual item representation. class CannotUseBinaryFilter < Generic # @param [Nanoc::Int::ItemRep] rep The item representation that was # attempted to be filtered # # @param [Class] filter_class The filter class that was used def initialize(rep, filter_class) super("The “#{filter_class.inspect}” filter cannot be used to filter the “#{rep.item.identifier}” item (rep “#{rep.name}”), because binary filters cannot be used on textual items. If you are getting this error for an item that should be textual instead of binary, make sure that its extension is included in the text_extensions array in the site configuration.") end end # Error that is raised when the compiled content at a non-existing snapshot # is requested. class NoSuchSnapshot < Generic # @return [Nanoc::Int::ItemRep] The item rep from which the compiled content # was requested attr_reader :item_rep # @return [Symbol] The requested snapshot attr_reader :snapshot # @param [Nanoc::Int::ItemRep] item_rep The item rep from which the compiled # content was requested # # @param [Symbol] snapshot The requested snapshot def initialize(item_rep, snapshot) @item_rep = item_rep @snapshot = snapshot super("The “#{item_rep.inspect}” item rep does not have a snapshot “#{snapshot.inspect}”") end end # Error that is raised when a snapshot with an existing name is made. class CannotCreateMultipleSnapshotsWithSameName < Generic # @param [Nanoc::Int::ItemRep] rep The item representation for which a # snapshot was attempted to be made # # @param [Symbol] snapshot The name of the snapshot that was attempted to # be made def initialize(rep, snapshot) super("Attempted to create a snapshot with a duplicate name #{snapshot.inspect} for the item rep “#{rep.inspect}”") end end # Error that is raised when the compiled content of a binary item is attempted to be accessed. class CannotGetCompiledContentOfBinaryItem < Generic # @param [Nanoc::Int::ItemRep] rep The binary item representation whose compiled content was attempted to be accessed def initialize(rep) super("You cannot access the compiled content of a binary item representation (but you can access the path). The offending item rep is #{rep.inspect}.") end end # Error that is raised when multiple items or layouts with the same identifier exist. class DuplicateIdentifier < Generic def initialize(identifier, type) super("There are multiple #{type}s with the #{identifier} identifier.") end end # Error that is raised when attempting to call #parent or #children on an item with a legacy identifier. class CannotGetParentOrChildrenOfNonLegacyItem < Generic def initialize(identifier) super("You cannot get the parent or children of an item that has a “full” identifier (#{identifier}). Getting the parent or children of an item is only possible for items that have a legacy identifier.") end end end end nanoc-4.1.4/lib/nanoc/base/entities/0000755000004100000410000000000012665031555017260 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/entities/code_snippet.rb0000644000004100000410000000210712665031555022261 0ustar www-datawww-datamodule Nanoc::Int # Nanoc::Int::CodeSnippet represent a piece of custom code of a Nanoc site. # # @api private class CodeSnippet # A string containing the actual code in this code snippet. # # @return [String] attr_reader :data # The filename corresponding to this code snippet. # # @return [String] attr_reader :filename # Creates a new code snippet. # # @param [String] data The raw source code which will be executed before # compilation # # @param [String] filename The filename corresponding to this code snippet def initialize(data, filename) @data = data @filename = filename end # Loads the code by executing it. # # @return [void] def load eval(@data, TOPLEVEL_BINDING, @filename) end # Returns an object that can be used for uniquely identifying objects. # # @return [Object] An unique reference to this object def reference [:code_snippet, filename] end def inspect "<#{self.class} filename=\"#{filename}\">" end end end nanoc-4.1.4/lib/nanoc/base/entities/rule_memory_actions/0000755000004100000410000000000012665031555023337 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/entities/rule_memory_actions/snapshot.rb0000644000004100000410000000120112665031555025515 0ustar www-datawww-datamodule Nanoc::Int::RuleMemoryActions class Snapshot < Nanoc::Int::RuleMemoryAction # snapshot :before_layout # snapshot :before_layout, final: true # snapshot :before_layout, path: '/about.md' attr_reader :snapshot_name attr_reader :final attr_reader :path alias final? final def initialize(snapshot_name, final, path) @snapshot_name = snapshot_name @final = final @path = path end def serialize [:snapshot, @snapshot_name, @final, @path] end def to_s "snapshot #{@snapshot_name.inspect}, final: #{@final.inspect}, path: #{@path.inspect}" end end end nanoc-4.1.4/lib/nanoc/base/entities/rule_memory_actions/filter.rb0000644000004100000410000000073612665031555025157 0ustar www-datawww-datamodule Nanoc::Int::RuleMemoryActions class Filter < Nanoc::Int::RuleMemoryAction # filter :foo # filter :foo, params attr_reader :filter_name attr_reader :params def initialize(filter_name, params) @filter_name = filter_name @params = params end def serialize [:filter, @filter_name, Nanoc::Int::Checksummer.calc(@params)] end def to_s "filter #{@filter_name.inspect}, #{@params.inspect}" end end end nanoc-4.1.4/lib/nanoc/base/entities/rule_memory_actions/layout.rb0000644000004100000410000000101112665031555025172 0ustar www-datawww-datamodule Nanoc::Int::RuleMemoryActions class Layout < Nanoc::Int::RuleMemoryAction # layout '/foo.erb' # layout '/foo.erb', params attr_reader :layout_identifier attr_reader :params def initialize(layout_identifier, params) @layout_identifier = layout_identifier @params = params end def serialize [:layout, @layout_identifier, Nanoc::Int::Checksummer.calc(@params)] end def to_s "layout #{@layout_identifier.inspect}, #{@params.inspect}" end end end nanoc-4.1.4/lib/nanoc/base/entities/identifiable_collection.rb0000644000004100000410000000310012665031555024431 0ustar www-datawww-datamodule Nanoc::Int # @api private class IdentifiableCollection include Enumerable extend Forwardable def_delegator :@objects, :each def_delegator :@objects, :size def_delegator :@objects, :<< def_delegator :@objects, :concat def initialize(config) @config = config @objects = [] end def freeze @objects.freeze @objects.each(&:freeze) build_mapping super end def [](arg) case arg when Nanoc::Identifier object_with_identifier(arg) when String object_with_identifier(arg) || object_matching_glob(arg) when Regexp @objects.find { |i| i.identifier.to_s =~ arg } else raise ArgumentError, "don’t know how to fetch objects by #{arg.inspect}" end end def to_a @objects end def empty? @objects.empty? end def delete_if(&block) @objects.delete_if(&block) end protected def object_with_identifier(identifier) if frozen? @mapping[identifier.to_s] else @objects.find { |i| i.identifier == identifier } end end def object_matching_glob(glob) if use_globs? pat = Nanoc::Int::Pattern.from(glob) @objects.find { |i| pat.match?(i.identifier) } else nil end end def build_mapping @mapping = {} @objects.each do |object| @mapping[object.identifier.to_s] = object end @mapping.freeze end def use_globs? @config[:string_pattern_type] == 'glob' end end end nanoc-4.1.4/lib/nanoc/base/entities/configuration.rb0000644000004100000410000000523512665031555022461 0ustar www-datawww-datamodule Nanoc::Int # Represents the site configuration. # # @api private class Configuration NONE = Object.new.freeze # The default configuration for a data source. A data source's # configuration overrides these options. DEFAULT_DATA_SOURCE_CONFIG = { type: 'filesystem', items_root: '/', layouts_root: '/', config: {}, identifier_type: 'full', }.freeze # The default configuration for a site. A site's configuration overrides # these options: when a {Nanoc::Int::Site} is created with a configuration # that lacks some options, the default value will be taken from # `DEFAULT_CONFIG`. DEFAULT_CONFIG = { text_extensions: %w( adoc asciidoc atom css erb haml htm html js less markdown md php rb sass scss txt xhtml xml coffee hb handlebars mustache ms slim rdoc ).sort, lib_dirs: %w( lib ), commands_dirs: %w( commands ), output_dir: 'output', data_sources: [{}], index_filenames: ['index.html'], enable_output_diff: false, prune: { auto_prune: false, exclude: ['.git', '.hg', '.svn', 'CVS'] }, string_pattern_type: 'glob', }.freeze # Creates a new configuration with the given hash. # # @param [Hash] hash The actual configuration hash def initialize(hash = {}) @wrapped = hash.__nanoc_symbolize_keys_recursively end def with_defaults new_wrapped = DEFAULT_CONFIG.merge(@wrapped) new_wrapped[:data_sources] = new_wrapped[:data_sources].map do |ds| DEFAULT_DATA_SOURCE_CONFIG.merge(ds) end self.class.new(new_wrapped) end def to_h @wrapped end def key?(key) @wrapped.key?(key) end def [](key) @wrapped[key] end def fetch(key, fallback = NONE, &_block) @wrapped.fetch(key) do if !fallback.equal?(NONE) fallback elsif block_given? yield(key) else raise KeyError, "key not found: #{key.inspect}" end end end def []=(key, value) @wrapped[key] = value end def merge(hash) self.class.new(@wrapped.merge(hash.to_h)) end def without(key) self.class.new(@wrapped.reject { |k, _v| k == key }) end def update(hash) @wrapped.update(hash) end def each @wrapped.each { |k, v| yield(k, v) } self end def freeze super @wrapped.__nanoc_freeze_recursively end # Returns an object that can be used for uniquely identifying objects. # # @return [Object] An unique reference to this object def reference :config end def inspect "<#{self.class}>" end end end nanoc-4.1.4/lib/nanoc/base/entities/pattern.rb0000644000004100000410000000242312665031555021263 0ustar www-datawww-datamodule Nanoc::Int # @api private class Pattern def self.from(obj) case obj when Nanoc::Int::StringPattern, Nanoc::Int::RegexpPattern obj when String Nanoc::Int::StringPattern.new(obj) when Regexp Nanoc::Int::RegexpPattern.new(obj) else raise ArgumentError, "Do not know how to convert `#{obj.inspect}` into a Nanoc::Pattern" end end def initialize(_obj) raise NotImplementedError end def match?(_identifier) raise NotImplementedError end def captures(_identifier) raise NotImplementedError end end # @api private class StringPattern def initialize(string) @string = string end def match?(identifier) opts = File::FNM_PATHNAME | File::FNM_EXTGLOB File.fnmatch(@string, identifier.to_s, opts) end def captures(_identifier) nil end def to_s @string end end # @api private class RegexpPattern def initialize(regexp) @regexp = regexp end def match?(identifier) (identifier.to_s =~ @regexp) != nil end def captures(identifier) matches = @regexp.match(identifier.to_s) matches && matches.captures end def to_s @regexp.to_s end end end nanoc-4.1.4/lib/nanoc/base/entities/item.rb0000644000004100000410000000161512665031555020546 0ustar www-datawww-datamodule Nanoc::Int # @api private class Item < ::Nanoc::Int::Document # @see Document#initialize def initialize(content, attributes, identifier) super @forced_outdated_status = ForcedOutdatedStatus.new end # Returns an object that can be used for uniquely identifying objects. # # @api private # # @return [Object] An unique reference to this object def reference [:item, identifier.to_s] end # Hack to allow a frozen item to still have modifiable frozen status. # # FIXME: Remove this. class ForcedOutdatedStatus attr_accessor :bool def initialize @bool = false end def freeze end end # @api private def forced_outdated=(bool) @forced_outdated_status.bool = bool end # @api private def forced_outdated? @forced_outdated_status.bool end end end nanoc-4.1.4/lib/nanoc/base/entities/document.rb0000644000004100000410000000230412665031555021422 0ustar www-datawww-datamodule Nanoc module Int # @api private class Document # @return [Nanoc::Int::Content] attr_reader :content # @return [Hash] attr_reader :attributes # @return [Nanoc::Identifier] attr_accessor :identifier # @param [String, Nanoc::Int::Content] content # # @param [Hash] attributes # # @param [String, Nanoc::Identifier] identifier def initialize(content, attributes, identifier) @content = Nanoc::Int::Content.create(content) @attributes = attributes.__nanoc_symbolize_keys_recursively @identifier = Nanoc::Identifier.from(identifier) end # @return [void] def freeze super attributes.__nanoc_freeze_recursively content.freeze end # @abstract # # @return Unique reference to this object def reference raise NotImplementedError end def inspect "<#{self.class} identifier=\"#{identifier}\">" end def hash self.class.hash ^ identifier.hash end def ==(other) other.respond_to?(:identifier) && identifier == other.identifier end alias eql? == end end end nanoc-4.1.4/lib/nanoc/base/entities/rule_memory_actions.rb0000644000004100000410000000021412665031555023661 0ustar www-datawww-datarequire_relative 'rule_memory_actions/filter' require_relative 'rule_memory_actions/layout' require_relative 'rule_memory_actions/snapshot' nanoc-4.1.4/lib/nanoc/base/entities/rule_memory_action.rb0000644000004100000410000000072012665031555023500 0ustar www-datawww-datamodule Nanoc::Int class RuleMemoryAction def serialize raise NotImplementedError.new('Nanoc::RuleMemoryAction subclasses must implement #serialize and #to_s') end def to_s raise NotImplementedError.new('Nanoc::RuleMemoryAction subclasses must implement #serialize and #to_s') end def inspect format( '<%s %s>', self.class.to_s, serialize[1..-1].map(&:inspect).join(', '), ) end end end nanoc-4.1.4/lib/nanoc/base/entities/site.rb0000644000004100000410000000303412665031555020551 0ustar www-datawww-datamodule Nanoc::Int # @api private class Site # @param [Nanoc::Int::Configuration] config # @param [Enumerable] code_snippets # @param [Enumerable] items # @param [Enumerable] layouts def initialize(config:, code_snippets:, items:, layouts:) @config = config @code_snippets = code_snippets @items = items @layouts = layouts ensure_identifier_uniqueness(@items, 'item') ensure_identifier_uniqueness(@layouts, 'layout') end # Compiles the site. # # @return [void] # # @since 3.2.0 def compile compiler.run_all end # Returns the compiler for this site. Will create a new compiler if none # exists yet. # # @return [Nanoc::Int::Compiler] The compiler for this site def compiler @compiler ||= Nanoc::Int::CompilerLoader.new.load(self) end attr_reader :code_snippets attr_reader :config attr_reader :items attr_reader :layouts # Prevents all further modifications to itself, its items, its layouts etc. # # @return [void] def freeze config.freeze items.freeze layouts.freeze code_snippets.__nanoc_freeze_recursively end def ensure_identifier_uniqueness(objects, type) seen = Set.new objects.each do |obj| if seen.include?(obj.identifier) raise Nanoc::Int::Errors::DuplicateIdentifier.new(obj.identifier, type) end seen << obj.identifier end end end end nanoc-4.1.4/lib/nanoc/base/entities/snapshot_def.rb0000644000004100000410000000035212665031555022262 0ustar www-datawww-datamodule Nanoc module Int class SnapshotDef attr_reader :name def initialize(name, is_final) @name = name @is_final = is_final end def final? @is_final end end end end nanoc-4.1.4/lib/nanoc/base/entities/identifier.rb0000644000004100000410000000737712665031555021745 0ustar www-datawww-datamodule Nanoc class Identifier include Comparable # @api private class InvalidIdentifierError < ::Nanoc::Error def initialize(string) super("Invalid identifier (does not start with a slash): #{string.inspect}") end end # @api private class InvalidTypeError < ::Nanoc::Error def initialize(type) super("Invalid type for identifier: #{type.inspect} (can be :full or :legacy)") end end # @api private class InvalidPrefixError < ::Nanoc::Error def initialize(string) super("Invalid prefix (does not start with a slash): #{string.inspect}") end end # @api private class UnsupportedLegacyOperationError < ::Nanoc::Error def initialize super('Cannot use this method on legacy identifiers') end end # @api private class NonCoercibleObjectError < ::Nanoc::Error def initialize(obj) super("#{obj.inspect} cannot be converted into a Nanoc::Identifier") end end def self.from(obj) case obj when Nanoc::Identifier obj when String Nanoc::Identifier.new(obj) else raise NonCoercibleObjectError.new(obj) end end def initialize(string, type: :full) @type = type case @type when :legacy @string = "/#{string}/".gsub(/^\/+|\/+$/, '/').freeze when :full if string !~ /\A\// raise InvalidIdentifierError.new(string) end @string = string.dup.freeze else raise InvalidTypeError.new(@type) end end def ==(other) case other when Nanoc::Identifier, String to_s == other.to_s else false end end alias eql? == def hash self.class.hash ^ to_s.hash end def =~(other) Nanoc::Int::Pattern.from(other).match?(to_s) ? 0 : nil end def <=>(other) to_s <=> other.to_s end # @return [Boolean] True if this is a full-type identifier (i.e. includes # the extension), false otherwise def full? @type == :full end # @return [Boolean] True if this is a legacy identifier (i.e. does not # include the extension), false otherwise def legacy? @type == :legacy end # @return [String] def chop to_s.chop end # @return [String] def +(other) to_s + other end # @return [Nanoc::Identifier] def prefix(string) if string !~ /\A\// raise InvalidPrefixError.new(@string) end Nanoc::Identifier.new(string.sub(/\/+\z/, '') + @string, type: @type) end # @return [String] def without_ext unless full? raise UnsupportedLegacyOperationError end extname = File.extname(@string) if !extname.empty? @string[0..-extname.size - 1] else @string end end # @return [String] The extension, without a leading dot. def ext unless full? raise UnsupportedLegacyOperationError end s = File.extname(@string) s && s[1..-1] end # @return [String] def without_exts extname = exts.join('.') if !extname.empty? @string[0..-extname.size - 2] else @string end end # @return [Array] List of extensions, without a leading dot. def exts unless full? raise UnsupportedLegacyOperationError end s = File.basename(@string) s ? s.split('.', -1).drop(1) : [] end def components res = to_s.split('/') if res.empty? [] else res[1..-1] end end def to_s @string end def to_str @string end def inspect "" end end end nanoc-4.1.4/lib/nanoc/base/entities/layout.rb0000644000004100000410000000021312665031555021116 0ustar www-datawww-datamodule Nanoc::Int # @api private class Layout < ::Nanoc::Int::Document def reference [:layout, identifier] end end end nanoc-4.1.4/lib/nanoc/base/entities/item_rep.rb0000644000004100000410000001061212665031555021411 0ustar www-datawww-datamodule Nanoc::Int # @api private class ItemRep # @return [Hash] attr_accessor :raw_paths # @return [Hash] attr_accessor :paths # @return [Nanoc::Int::Item] attr_reader :item # @return [Symbol] attr_reader :name # @return [Enumerable" end private def initialize_content # FIXME: Where is :raw? @snapshot_contents = { last: @item.content } end end end nanoc-4.1.4/lib/nanoc/base/entities/rule_memory.rb0000644000004100000410000000244612665031555022152 0ustar www-datawww-datamodule Nanoc::Int class RuleMemory include Enumerable def initialize(item_rep) @item_rep = item_rep @actions = [] end def size @actions.size end def [](idx) @actions[idx] end def add_filter(filter_name, params) @actions << Nanoc::Int::RuleMemoryActions::Filter.new(filter_name, params) end def add_layout(layout_identifier, params) @actions << Nanoc::Int::RuleMemoryActions::Layout.new(layout_identifier, params) end def add_snapshot(snapshot_name, final, path) will_add_snapshot(snapshot_name) if final @actions << Nanoc::Int::RuleMemoryActions::Snapshot.new(snapshot_name, final, path) end def snapshot_actions @actions.select { |a| a.is_a?(Nanoc::Int::RuleMemoryActions::Snapshot) } end def any_layouts? @actions.any? { |a| a.is_a?(Nanoc::Int::RuleMemoryActions::Layout) } end def serialize map(&:serialize) end def each @actions.each { |a| yield(a) } end private def will_add_snapshot(name) @_snapshot_names ||= Set.new if @_snapshot_names.include?(name) raise Nanoc::Int::Errors::CannotCreateMultipleSnapshotsWithSameName.new(@item_rep, name) else @_snapshot_names << name end end end end nanoc-4.1.4/lib/nanoc/base/entities/content.rb0000644000004100000410000000367412665031555021271 0ustar www-datawww-datamodule Nanoc module Int # Abstract content. # # The filename is the full filename on the default filesystem. It can be # nil. It is used by filters such as Sass, which look up items on the # filesystem. # # @abstract # # @api private class Content # @return [String, nil] attr_reader :filename # @param [String, nil] filename def initialize(filename) if filename && Pathname.new(filename).relative? raise ArgumentError, 'Content filename is not absolute' end @filename = filename end def freeze super @filename.freeze end # @param [String] content The uncompiled item content (if it is textual # content) or the path to the filename containing the content (if this # is binary content). # # @param [Boolean] binary Whether or not this item is binary # # @param [String] filename Absolute path to the file containing this # content (if any) def self.create(content, binary: false, filename: nil) if content.nil? raise ArgumentError, 'Cannot create nil content' elsif content.is_a?(Nanoc::Int::Content) content elsif binary Nanoc::Int::BinaryContent.new(content) else Nanoc::Int::TextualContent.new(content, filename: filename) end end # @abstract # # @return [Boolean] def binary? raise NotImplementedError end end # @api private class TextualContent < Content # @return [String] attr_reader :string def initialize(string, filename: nil) super(filename) @string = string end def freeze super @string.freeze end def binary? false end end # @api private class BinaryContent < Content def binary? true end end end end nanoc-4.1.4/lib/nanoc/base/context.rb0000644000004100000410000000255412665031555017453 0ustar www-datawww-datamodule Nanoc::Int # Provides a context and a binding for use in filters such as the ERB and # Haml ones. # # @api private class Context # Creates a new context based off the contents of the hash. # # Each pair in the hash will be converted to an instance variable and an # instance method. For example, passing the hash `{ :foo => 'bar' }` will # cause `@foo` to have the value `"bar"`, and the instance method `#foo` # to return the same value `"bar"`. # # @param [Hash] hash A list of key-value pairs to make available # # @example Defining a context and accessing values # # context = Nanoc::Int::Context.new( # :name => 'Max Payne', # :location => 'in a cheap motel' # ) # context.instance_eval do # "I am #{name} and I am hiding #{@location}." # end # # => "I am Max Payne and I am hiding in a cheap motel." def initialize(hash) hash.each_pair do |key, value| # Build instance variable instance_variable_set('@' + key.to_s, value) # Define method metaclass = (class << self; self; end) metaclass.send(:define_method, key) { value } end end # Returns a binding for this instance. # # @return [Binding] A binding for this instance def get_binding binding end end end nanoc-4.1.4/lib/nanoc/base/compilation/0000755000004100000410000000000012665031555017752 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/compilation/item_rep_repo.rb0000644000004100000410000000073512665031555023135 0ustar www-datawww-datamodule Nanoc::Int # Stores item reps (in memory). # # @api private class ItemRepRepo include Enumerable def initialize @reps = [] @reps_by_item = {} end def <<(rep) @reps << rep @reps_by_item[rep.item] ||= [] @reps_by_item[rep.item] << rep end def to_a @reps end def each(&block) @reps.each(&block) self end def [](item) @reps_by_item.fetch(item, []) end end end nanoc-4.1.4/lib/nanoc/base/compilation/outdatedness_checker.rb0000644000004100000410000001670712665031555024500 0ustar www-datawww-datamodule Nanoc::Int # Responsible for determining whether an item or a layout is outdated. # # @api private class OutdatednessChecker extend Nanoc::Int::Memoization attr_reader :checksum_store attr_reader :dependency_store attr_reader :rule_memory_store attr_reader :site Reasons = Nanoc::Int::OutdatednessReasons # @param [Nanoc::Int::Site] site # @param [Nanoc::Int::ChecksumStore] checksum_store # @param [Nanoc::Int::DependencyStore] dependency_store # @param [Nanoc::Int::RuleMemoryStore] rule_memory_store # @param [Nanoc::Int::ActionProvider] action_provider # @param [Nanoc::Int::ItemRepRepo] reps def initialize(site:, checksum_store:, dependency_store:, rule_memory_store:, action_provider:, reps:) @site = site @checksum_store = checksum_store @dependency_store = dependency_store @rule_memory_store = rule_memory_store @action_provider = action_provider @reps = reps @basic_outdatedness_reasons = {} @outdatedness_reasons = {} @objects_outdated_due_to_dependencies = {} end # Checks whether the given object is outdated and therefore needs to be # recompiled. # # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object # whose outdatedness should be checked. # # @return [Boolean] true if the object is outdated, false otherwise def outdated?(obj) !outdatedness_reason_for(obj).nil? end # Calculates the reason why the given object is outdated. # # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object # whose outdatedness reason should be calculated. # # @return [Reasons::Generic, nil] The reason why the # given object is outdated, or nil if the object is not outdated. def outdatedness_reason_for(obj) reason = basic_outdatedness_reason_for(obj) if reason.nil? && outdated_due_to_dependencies?(obj) reason = Reasons::DependenciesOutdated end reason end memoize :outdatedness_reason_for private # Checks whether the given object is outdated and therefore needs to be # recompiled. This method does not take dependencies into account; use # {#outdated?} if you want to include dependencies in the outdatedness # check. # # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object # whose outdatedness should be checked. # # @return [Boolean] true if the object is outdated, false otherwise def basic_outdated?(obj) !basic_outdatedness_reason_for(obj).nil? end # Calculates the reason why the given object is outdated. This method does # not take dependencies into account; use {#outdatedness_reason_for?} if # you want to include dependencies in the outdatedness check. # # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object # whose outdatedness reason should be calculated. # # @return [Reasons::Generic, nil] The reason why the # given object is outdated, or nil if the object is not outdated. def basic_outdatedness_reason_for(obj) case obj when Nanoc::Int::ItemRep # Outdated if rules outdated return Reasons::RulesModified if rule_memory_differs_for(obj) # Outdated if checksums are missing or different return Reasons::NotEnoughData unless checksums_available?(obj.item) return Reasons::SourceModified unless checksums_identical?(obj.item) # Outdated if compiled file doesn't exist (yet) return Reasons::NotWritten if obj.raw_path && !File.file?(obj.raw_path) # Outdated if code snippets outdated return Reasons::CodeSnippetsModified if site.code_snippets.any? do |cs| object_modified?(cs) end # Outdated if configuration outdated return Reasons::ConfigurationModified if object_modified?(site.config) # Not outdated return nil when Nanoc::Int::Item @reps[obj].find { |rep| basic_outdatedness_reason_for(rep) } when Nanoc::Int::Layout # Outdated if rules outdated return Reasons::RulesModified if rule_memory_differs_for(obj) # Outdated if checksums are missing or different return Reasons::NotEnoughData unless checksums_available?(obj) return Reasons::SourceModified unless checksums_identical?(obj) # Not outdated return nil else raise "do not know how to check outdatedness of #{obj.inspect}" end end memoize :basic_outdatedness_reason_for # Checks whether the given object is outdated due to dependencies. # # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object # whose outdatedness should be checked. # # @param [Set] processed The collection of items that has been visited # during this outdatedness check. This is used to prevent checks for # items that (indirectly) depend on their own from looping # indefinitely. It should not be necessary to pass this a custom value. # # @return [Boolean] true if the object is outdated, false otherwise def outdated_due_to_dependencies?(obj, processed = Set.new) # Convert from rep to item if necessary obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep) # Get from cache if @objects_outdated_due_to_dependencies.key?(obj) return @objects_outdated_due_to_dependencies[obj] end # Check processed # Don’t return true; the false will be or’ed into a true if there # really is a dependency that is causing outdatedness. return false if processed.include?(obj) # Calculate is_outdated = dependency_store.objects_causing_outdatedness_of(obj).any? do |other| other.nil? || basic_outdated?(other) || outdated_due_to_dependencies?(other, processed.merge([obj])) end # Cache @objects_outdated_due_to_dependencies[obj] = is_outdated # Done is_outdated end # @param [Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The layout or item # representation to check the rule memory for # # @return [Boolean] true if the rule memory for the given item # represenation has changed, false otherwise def rule_memory_differs_for(obj) !rule_memory_store[obj].eql?(@action_provider.memory_for(obj).serialize) end memoize :rule_memory_differs_for # @param obj The object to create a checksum for # # @return [String] The digest def calc_checksum(obj) Nanoc::Int::Checksummer.calc(obj) end memoize :calc_checksum # @param obj # # @return [Boolean] false if either the new or the old checksum for the # given object is not available, true if both checksums are available def checksums_available?(obj) checksum_store[obj] && calc_checksum(obj) end memoize :checksums_available? # @param obj # # @return [Boolean] false if the old and new checksums for the given # object differ, true if they are identical def checksums_identical?(obj) checksum_store[obj] == calc_checksum(obj) end memoize :checksums_identical? # @param obj # # @return [Boolean] true if the old and new checksums for the given object # are available and identical, false otherwise def object_modified?(obj) !checksums_available?(obj) || !checksums_identical?(obj) end memoize :object_modified? end end nanoc-4.1.4/lib/nanoc/base/compilation/filter.rb0000644000004100000410000001443412665031555021572 0ustar www-datawww-datamodule Nanoc # Nanoc::Filter is responsible for filtering items. It is the superclass # for all textual filters. # # A filter instance should only be used once. Filters should not be reused # since they store state. # # When creating a filter with a hash containing assigned variables, those # variables will be made available in the `@assigns` instance variable and # the {#assigns} method. The assigns itself will also be available as # instance variables and instance methods. # # @example Accessing assigns in different ways # # filter = SomeFilter.new({ :foo => 'bar' }) # filter.instance_eval { @assigns[:foo] } # # => 'bar' # filter.instance_eval { assigns[:foo] } # # => 'bar' # filter.instance_eval { @foo } # # => 'bar' # filter.instance_eval { foo } # # => 'bar' # # @abstract Subclass and override {#run} to implement a custom filter. class Filter < Nanoc::Int::Context # @api private TMP_BINARY_ITEMS_DIR = 'binary_items'.freeze # A hash containing variables that will be made available during # filtering. # # @return [Hash] # # @api private attr_reader :assigns extend Nanoc::Int::PluginRegistry::PluginMethods class << self # Sets the new type for the filter. The type can be `:binary` (default) # or `:text`. The given argument can either be a symbol indicating both # “from” and “to” types, or a hash where the only key is the “from” type # and the only value is the “to” type. # # @example Specifying a text-to-text filter # # type :text # # @example Specifying a text-to-binary filter # # type :text => :binary # # @param [Symbol, Hash] arg The new type of this filter # # @return [void] def type(arg) if arg.is_a?(Hash) @from = arg.keys[0] @to = arg.values[0] else @from = arg @to = arg end end # @return [Boolean] True if this filter can be applied to binary item # representations, false otherwise # # @api private def from_binary? (@from || :text) == :binary end # @return [Boolean] True if this filter results in a binary item # representation, false otherwise # # @api private def to_binary? (@to || :text) == :binary end # @overload requires(*requires) # Sets the required libraries for this filter. # @param [Array] requires A list of library names that are required # @return [void] # @overload requires # Returns the required libraries for this filter. # @return [Enumerable] This filter’s list of library names that are required def requires(*requires) if requires.any? @requires = requires else @requires || [] end end # Requires the filter’s required library if necessary. # # @return [void] # # @api private def setup @setup ||= begin requires.each { |r| require r } true end end end # Creates a new filter that has access to the given assigns. # # @param [Hash] hash A hash containing variables that should be made # available during filtering. # # @api private def initialize(hash = {}) @assigns = hash super end # Sets up the filter and runs the filter. This method passes its arguments # to {#run} unchanged and returns the return value from {#run}. # # @see {#run} # # @api private def setup_and_run(*args) self.class.setup run(*args) end # Runs the filter on the given content or filename. # # @abstract # # @param [String] content_or_filename The unprocessed content that should # be filtered (if the item is a textual item) or the path to the file # that should be filtered (if the item is a binary item) # # @param [Hash] params A hash containing parameters. Filter subclasses can # use these parameters to allow modifying the filter's behaviour. # # @return [String, void] If the filter output binary content, the return # value is undefined; if the filter outputs textual content, the return # value will be the filtered content. def run(content_or_filename, params = {}) # rubocop:disable Lint/UnusedMethodArgument raise NotImplementedError.new('Nanoc::Filter subclasses must implement #run') end # Returns a filename that is used to write data to. This method is only # used on binary items. When running a binary filter on a file, the # resulting file must end up in the location returned by this method. # # The returned filename will be absolute, so it is safe to change to # another directory inside the filter. # # @return [String] The output filename def output_filename @output_filename ||= Nanoc::Int::TempFilenameFactory.instance.create(TMP_BINARY_ITEMS_DIR) end # Returns the filename associated with the item that is being filtered. # It is in the format `item (rep )`. # # @return [String] The filename # # @api private def filename if assigns[:layout] "layout #{assigns[:layout].identifier}" elsif assigns[:item] "item #{assigns[:item].identifier} (rep #{assigns[:item_rep].name})" else '?' end end # Creates a dependency from the item that is currently being filtered onto # the given collection of items. In other words, require the given items # to be compiled first before this items is processed. # # @return [void] def depend_on(items) orig_items = items items = items.map { |i| i.is_a?(Nanoc::ItemWithRepsView) ? i.unwrap : i } # Notify items.each do |item| Nanoc::Int::NotificationCenter.post(:visit_started, item) Nanoc::Int::NotificationCenter.post(:visit_ended, item) end # Raise unmet dependency error if necessary items.each do |item| rep = orig_items.sample._context.reps[item].find { |r| !r.compiled? } raise Nanoc::Int::Errors::UnmetDependency.new(rep) if rep end end end end nanoc-4.1.4/lib/nanoc/base/compilation/outdatedness_reasons.rb0000644000004100000410000000273212665031555024537 0ustar www-datawww-datamodule Nanoc::Int # Module that contains all outdatedness reasons. # # @api private module OutdatednessReasons # A generic outdatedness reason. An outdatedness reason is basically a # descriptive message that explains why a given object is outdated. class Generic # @return [String] A descriptive message for this outdatedness reason attr_reader :message # @param [String] message The descriptive message for this outdatedness # reason def initialize(message) @message = message end end CodeSnippetsModified = Generic.new( 'The code snippets have been modified since the last time the site was compiled.') ConfigurationModified = Generic.new( 'The site configuration has been modified since the last time the site was compiled.') DependenciesOutdated = Generic.new( 'This item uses content or attributes that have changed since the last time the site was compiled.') NotEnoughData = Generic.new( 'Not enough data is present to correctly determine whether the item is outdated.') NotWritten = Generic.new( 'This item representation has not yet been written to the output directory (but it does have a path).') RulesModified = Generic.new( 'The rules file has been modified since the last time the site was compiled.') SourceModified = Generic.new( 'The source file of this item has been modified since the last time the site was compiled.') end end nanoc-4.1.4/lib/nanoc/base/compilation/compiler.rb0000644000004100000410000002276712665031555022127 0ustar www-datawww-datamodule Nanoc::Int # Responsible for compiling a site’s item representations. # # The compilation process makes use of notifications (see # {Nanoc::Int::NotificationCenter}) to track dependencies between items, # layouts, etc. The following notifications are used: # # * `compilation_started` — indicates that the compiler has started # compiling this item representation. Has one argument: the item # representation itself. Only one item can be compiled at a given moment; # therefore, it is not possible to get two consecutive # `compilation_started` notifications without also getting a # `compilation_ended` notification in between them. # # * `compilation_ended` — indicates that the compiler has finished compiling # this item representation (either successfully or with failure). Has one # argument: the item representation itself. # # * `visit_started` — indicates that the compiler requires content or # attributes from the item representation that will be visited. Has one # argument: the visited item identifier. This notification is used to # track dependencies of items on other items; a `visit_started` event # followed by another `visit_started` event indicates that the item # corresponding to the former event will depend on the item from the # latter event. # # * `visit_ended` — indicates that the compiler has finished visiting the # item representation and that the requested attributes or content have # been fetched (either successfully or with failure) # # * `processing_started` — indicates that the compiler has started # processing the specified object, which can be an item representation # (when it is compiled) or a layout (when it is used to lay out an item # representation or when it is used as a partial) # # * `processing_ended` — indicates that the compiler has finished processing # the specified object. # # @api private class Compiler # @api private attr_reader :site # The compilation stack. When the compiler begins compiling a rep or a # layout, it will be placed on the stack; when it is done compiling the # rep or layout, it will be removed from the stack. # # @return [Array] The compilation stack attr_reader :stack # @api private attr_reader :compiled_content_cache # @api private attr_reader :checksum_store # @api private attr_reader :rule_memory_store # @api private attr_reader :action_provider # @api private attr_reader :dependency_store # @api private attr_reader :outdatedness_checker # @api private attr_reader :reps def initialize(site, compiled_content_cache:, checksum_store:, rule_memory_store:, action_provider:, dependency_store:, outdatedness_checker:, reps:) @site = site @compiled_content_cache = compiled_content_cache @checksum_store = checksum_store @rule_memory_store = rule_memory_store @dependency_store = dependency_store @outdatedness_checker = outdatedness_checker @reps = reps @action_provider = action_provider @stack = [] end def run_all @action_provider.preprocess(@site) build_reps run @action_provider.postprocess(@site, @reps) end def run load_stores @site.freeze # Determine which reps need to be recompiled forget_dependencies_if_outdated @stack = [] dependency_tracker = Nanoc::Int::DependencyTracker.new(@dependency_store) dependency_tracker.run do compile_reps end store ensure Nanoc::Int::TempFilenameFactory.instance.cleanup( Nanoc::Filter::TMP_BINARY_ITEMS_DIR) Nanoc::Int::TempFilenameFactory.instance.cleanup( Nanoc::Int::ItemRepWriter::TMP_TEXT_ITEMS_DIR) end def load_stores stores.each(&:load) end # Store the modified helper data used for compiling the site. # # @return [void] def store # Calculate rule memory (@reps.to_a + @site.layouts.to_a).each do |obj| rule_memory_store[obj] = action_provider.memory_for(obj).serialize end # Calculate checksums objects_to_checksum = site.items.to_a + site.layouts.to_a + site.code_snippets + [site.config] objects_to_checksum.each do |obj| checksum_store[obj] = Nanoc::Int::Checksummer.calc(obj) end # Store stores.each(&:store) end def build_reps builder = Nanoc::Int::ItemRepBuilder.new( site, action_provider, @reps) builder.run end # @param [Nanoc::Int::ItemRep] rep The item representation for which the # assigns should be fetched # # @return [Hash] The assigns that should be used in the next filter/layout # operation # # @api private def assigns_for(rep) content_or_filename_assigns = if rep.binary? { filename: rep.snapshot_contents[:last].filename } else { content: rep.snapshot_contents[:last].string } end view_context = create_view_context # TODO: Do not expose @site (necessary for captures store though…) content_or_filename_assigns.merge( item: Nanoc::ItemWithRepsView.new(rep.item, view_context), rep: Nanoc::ItemRepView.new(rep, view_context), item_rep: Nanoc::ItemRepView.new(rep, view_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), ) end def create_view_context Nanoc::ViewContext.new(reps: @reps, items: @site.items) end # @api private def filter_name_and_args_for_layout(layout) mem = action_provider.memory_for(layout) if mem.nil? || mem.size != 1 || !mem[0].is_a?(Nanoc::Int::RuleMemoryActions::Filter) # FIXME: Provide a nicer error message raise Nanoc::Int::Errors::Generic, "No rule memory found for #{layout.identifier}" end [mem[0].filter_name, mem[0].params] end private def compile_reps # Listen to processing start/stop Nanoc::Int::NotificationCenter.on(:processing_started, self) { |obj| @stack.push(obj) } Nanoc::Int::NotificationCenter.on(:processing_ended, self) { |_obj| @stack.pop } # Assign snapshots @reps.each do |rep| rep.snapshot_defs = action_provider.snapshots_defs_for(rep) end # Find item reps to compile and compile them selector = Nanoc::Int::ItemRepSelector.new(@reps) selector.each do |rep| @stack = [] compile_rep(rep) end ensure Nanoc::Int::NotificationCenter.remove(:processing_started, self) Nanoc::Int::NotificationCenter.remove(:processing_ended, self) end # Compiles the given item representation. # # This method should not be called directly; please use # {Nanoc::Int::Compiler#run} instead, and pass this item representation's item # as its first argument. # # @param [Nanoc::Int::ItemRep] rep The rep that is to be compiled # # @return [void] def compile_rep(rep) Nanoc::Int::NotificationCenter.post(:compilation_started, rep) Nanoc::Int::NotificationCenter.post(:processing_started, rep) Nanoc::Int::NotificationCenter.post(:visit_started, rep.item) if can_reuse_content_for_rep?(rep) Nanoc::Int::NotificationCenter.post(:cached_content_used, rep) rep.snapshot_contents = compiled_content_cache[rep] else recalculate_content_for_rep(rep) end rep.compiled = true compiled_content_cache[rep] = rep.snapshot_contents Nanoc::Int::NotificationCenter.post(:processing_ended, rep) Nanoc::Int::NotificationCenter.post(:compilation_ended, rep) rescue => e rep.forget_progress Nanoc::Int::NotificationCenter.post(:compilation_failed, rep, e) raise e ensure Nanoc::Int::NotificationCenter.post(:visit_ended, rep.item) end # @return [Boolean] def can_reuse_content_for_rep?(rep) !rep.item.forced_outdated? && !outdatedness_checker.outdated?(rep) && compiled_content_cache[rep] end # @return [void] def recalculate_content_for_rep(rep) executor = Nanoc::Int::Executor.new(self) action_provider.memory_for(rep).each do |action| case action when Nanoc::Int::RuleMemoryActions::Filter executor.filter(rep, action.filter_name, action.params) when Nanoc::Int::RuleMemoryActions::Layout executor.layout(rep, action.layout_identifier, action.params) when Nanoc::Int::RuleMemoryActions::Snapshot executor.snapshot(rep, action.snapshot_name, final: action.final?, path: action.path) else raise "Internal inconsistency: unknown action #{action.inspect}" end end end # Clears the list of dependencies for items that will be recompiled. # # @return [void] def forget_dependencies_if_outdated @site.items.each do |i| if @reps[i].any? { |r| outdatedness_checker.outdated?(r) } @dependency_store.forget_dependencies_for(i) end end end # Returns all stores that can load/store data that can be used for # compilation. def stores [ checksum_store, compiled_content_cache, @dependency_store, rule_memory_store, ] end end end nanoc-4.1.4/lib/nanoc/base/compilation/dependency_tracker.rb0000644000004100000410000000227612665031555024137 0ustar www-datawww-datamodule Nanoc::Int # @api private class DependencyTracker def initialize(dependency_store) @dependency_store = dependency_store end # Record dependencies for the duration of the block. # # @return [void] def run unless block_given? raise ArgumentError, 'No block given' end stack = [] start_tracking(stack) yield ensure stop_tracking(stack) end # @api private def start_tracking(stack) Nanoc::Int::NotificationCenter.on(:visit_started, self) do |obj| unless stack.empty? Nanoc::Int::NotificationCenter.post(:dependency_created, stack.last, obj) @dependency_store.record_dependency(stack.last, obj) end stack.push(obj) end Nanoc::Int::NotificationCenter.on(:visit_ended, self) do |_obj| stack.pop end end # @api private def stop_tracking(stack) unless stack.empty? raise 'Internal inconsistency: dependency tracker stack not empty at end of compilation' end Nanoc::Int::NotificationCenter.remove(:visit_started, self) Nanoc::Int::NotificationCenter.remove(:visit_ended, self) end end end nanoc-4.1.4/lib/nanoc/base/services/0000755000004100000410000000000012665031555017257 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/services/item_rep_selector.rb0000644000004100000410000000161112665031555023307 0ustar www-datawww-datamodule Nanoc::Int # Yields item reps to compile. # # @api private class ItemRepSelector def initialize(reps) @reps = reps end def each graph = Nanoc::Int::DirectedGraph.new(@reps) loop do break if graph.roots.empty? rep = graph.roots.each { |e| break e } begin yield(rep) graph.delete_vertex(rep) rescue Nanoc::Int::Errors::UnmetDependency => e handle_dependency_error(e, rep, graph) end end # Check whether everything was compiled unless graph.vertices.empty? raise Nanoc::Int::Errors::RecursiveCompilation.new(graph.vertices) end end def handle_dependency_error(e, rep, graph) other_rep = e.rep graph.add_edge(other_rep, rep) unless graph.vertices.include?(other_rep) graph.add_vertex(other_rep) end end end end nanoc-4.1.4/lib/nanoc/base/services/item_rep_builder.rb0000644000004100000410000000076212665031555023123 0ustar www-datawww-datamodule Nanoc::Int # @api private class ItemRepBuilder attr_reader :reps def initialize(site, action_provider, reps) @site = site @action_provider = action_provider @reps = reps end def run @site.items.each do |item| @action_provider.rep_names_for(item).each do |rep_name| @reps << Nanoc::Int::ItemRep.new(item, rep_name) end end Nanoc::Int::ItemRepRouter.new(@reps, @action_provider, @site).run end end end nanoc-4.1.4/lib/nanoc/base/services/item_rep_writer.rb0000644000004100000410000000210612665031555023003 0ustar www-datawww-datamodule Nanoc::Int # @api private class ItemRepWriter TMP_TEXT_ITEMS_DIR = 'text_items'.freeze def write(item_rep, raw_path) # Create parent directory FileUtils.mkdir_p(File.dirname(raw_path)) # Check if file will be created is_created = !File.file?(raw_path) # Notify Nanoc::Int::NotificationCenter.post( :will_write_rep, item_rep, raw_path) content = item_rep.snapshot_contents[:last] if content.binary? temp_path = content.filename else temp_path = temp_filename File.write(temp_path, content.string) end # Check whether content was modified is_modified = is_created || !FileUtils.identical?(raw_path, temp_path) # Write FileUtils.cp(temp_path, raw_path) if is_modified item_rep.modified = is_modified # Notify Nanoc::Int::NotificationCenter.post( :rep_written, item_rep, raw_path, is_created, is_modified) end def temp_filename Nanoc::Int::TempFilenameFactory.instance.create(TMP_TEXT_ITEMS_DIR) end end end nanoc-4.1.4/lib/nanoc/base/services/notification_center.rb0000644000004100000410000000516612665031555023642 0ustar www-datawww-datamodule Nanoc::Int # Provides a way to send notifications between objects. It allows blocks # associated with a certain notification name to be registered; these blocks # will be called when the notification with the given name is posted. # # It is a slightly different implementation of the Observer pattern; the # table of subscribers is not stored in the observable object itself, but in # the notification center. # # @api private class NotificationCenter class << self # Adds the given block to the list of blocks that should be called when # the notification with the given name is received. # # @param [String, Symbol] name The name of the notification that will # cause the given block to be called. # # @param [String, Symbol, nil] id An identifier for the block. This is # only used to be able to remove the block (using the remove method) # later. Can be nil, but this is not recommended because it prevents # the given notification block from being unregistered. # # @yield [*args] Will be executed with the arguments passed to {.post} # # @return [void] def on(name, id = nil, &block) initialize_if_necessary(name) # Add observer @notifications[name] << { id: id, block: block } end # Posts a notification with the given name and the given arguments. # # @param [String, Symbol] name The name of the notification that should # be posted. # # @param args Arguments that wil be passed to the blocks handling the # notification. # # @return [void] def post(name, *args) initialize_if_necessary(name) # Notify all observers @notifications[name].each do |observer| observer[:block].call(*args) end end # Removes the block with the given identifier from the list of blocks # that should be called when the notification with the given name is # posted. # # @param [String, Symbol] name The name of the notification that should # no longer be registered. # # @param [String, Symbol] id The identifier of the block that should be # removed. # # @return [void] def remove(name, id) initialize_if_necessary(name) # Remove relevant observers @notifications[name].reject! { |i| i[:id] == id } end private def initialize_if_necessary(name) @notifications ||= {} # name => observers dictionary @notifications[name] ||= [] # list of observers end end end end nanoc-4.1.4/lib/nanoc/base/services/compiler_loader.rb0000644000004100000410000000222512665031555022745 0ustar www-datawww-datamodule Nanoc::Int # @api private class CompilerLoader def load(site) rule_memory_store = Nanoc::Int::RuleMemoryStore.new dependency_store = Nanoc::Int::DependencyStore.new(site.items.to_a + site.layouts.to_a) checksum_store = Nanoc::Int::ChecksumStore.new(site: site) item_rep_repo = Nanoc::Int::ItemRepRepo.new action_provider = Nanoc::Int::ActionProvider.named(:rule_dsl).for(site) outdatedness_checker = Nanoc::Int::OutdatednessChecker.new( site: site, checksum_store: checksum_store, dependency_store: dependency_store, rule_memory_store: rule_memory_store, action_provider: action_provider, reps: item_rep_repo, ) params = { compiled_content_cache: Nanoc::Int::CompiledContentCache.new, checksum_store: checksum_store, rule_memory_store: rule_memory_store, dependency_store: dependency_store, outdatedness_checker: outdatedness_checker, reps: item_rep_repo, action_provider: action_provider, } Nanoc::Int::Compiler.new(site, params) end end end nanoc-4.1.4/lib/nanoc/base/services/item_rep_router.rb0000644000004100000410000000303312665031555023007 0ustar www-datawww-datamodule Nanoc::Int # Assigns paths to reps. # # @api private class ItemRepRouter class IdenticalRoutesError < ::Nanoc::Error def initialize(output_path, rep_a, rep_b) super("The item representations #{rep_a.inspect} and #{rep_b.inspect} are both routed to #{output_path}.") end end def initialize(reps, action_provider, site) @reps = reps @action_provider = action_provider @site = site end def run paths_to_reps = {} @reps.each do |rep| mem = @action_provider.memory_for(rep) mem.snapshot_actions.each do |snapshot_action| route_rep(rep, snapshot_action, paths_to_reps) end end end def route_rep(rep, snapshot_action, paths_to_reps) basic_path = snapshot_action.path return if basic_path.nil? # Check for duplicate paths if paths_to_reps.key?(basic_path) raise IdenticalRoutesError.new(basic_path, paths_to_reps[basic_path], rep) else paths_to_reps[basic_path] = rep end rep.raw_paths[snapshot_action.snapshot_name] = @site.config[:output_dir] + basic_path rep.paths[snapshot_action.snapshot_name] = strip_index_filename(basic_path) end def strip_index_filename(basic_path) @site.config[:index_filenames].each do |index_filename| rep_path_ending = basic_path[-index_filename.length..-1] next unless rep_path_ending == index_filename return basic_path[0..-index_filename.length - 1] end basic_path end end end nanoc-4.1.4/lib/nanoc/base/services/action_provider.rb0000644000004100000410000000061012665031555022770 0ustar www-datawww-datamodule Nanoc::Int # @private class ActionProvider extend Nanoc::Int::PluginRegistry::PluginMethods def self.for(_site) raise NotImplementedError end def rep_names_for(_item) raise NotImplementedError end def memory_for(_rep) raise NotImplementedError end def snapshots_defs_for(_rep) raise NotImplementedError end end end nanoc-4.1.4/lib/nanoc/base/services/executor.rb0000644000004100000410000001177512665031555021455 0ustar www-datawww-datamodule Nanoc module Int class Executor class OutputNotWrittenError < ::Nanoc::Error def initialize(filter_name, output_filename) super("The #{filter_name.inspect} filter did not write anything to the required output file, #{output_filename}.") end end def initialize(compiler) @compiler = compiler end def filter(rep, filter_name, filter_args = {}) # Get filter class klass = Nanoc::Filter.named(filter_name) raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if klass.nil? # Check whether filter can be applied if klass.from_binary? && !rep.binary? raise Nanoc::Int::Errors::CannotUseBinaryFilter.new(rep, klass) elsif !klass.from_binary? && rep.binary? raise Nanoc::Int::Errors::CannotUseTextualFilter.new(rep, klass) end begin # Notify start Nanoc::Int::NotificationCenter.post(:filtering_started, rep, filter_name) # Create filter filter = klass.new(assigns_for(rep)) # Run filter last = rep.snapshot_contents[:last] source = rep.binary? ? last.filename : last.string result = filter.setup_and_run(source, filter_args) rep.snapshot_contents[:last] = if klass.to_binary? Nanoc::Int::BinaryContent.new(filter.output_filename).tap(&:freeze) else Nanoc::Int::TextualContent.new(result).tap(&:freeze) end # Check whether file was written if klass.to_binary? && !File.file?(filter.output_filename) raise OutputNotWrittenError.new(filter_name, filter.output_filename) end # Create snapshot snapshot(rep, rep.snapshot_contents[:post] ? :post : :pre, final: false) unless rep.binary? ensure # Notify end Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, filter_name) end end def layout(rep, layout_identifier, extra_filter_args = nil) layout = find_layout(layout_identifier) filter_name, filter_args = *@compiler.filter_name_and_args_for_layout(layout) if filter_name.nil? raise Nanoc::Int::Errors::Generic, "Cannot find rule for layout matching #{layout_identifier}" end filter_args = filter_args.merge(extra_filter_args || {}) # Check whether item can be laid out raise Nanoc::Int::Errors::CannotLayoutBinaryItem.new(rep) if rep.binary? # Create "pre" snapshot if rep.snapshot_contents[:post].nil? snapshot(rep, :pre, final: true) end # Create filter klass = Nanoc::Filter.named(filter_name) raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if klass.nil? layout_view = Nanoc::LayoutView.new(layout, nil) filter = klass.new(assigns_for(rep).merge({ layout: layout_view })) # Visit Nanoc::Int::NotificationCenter.post(:visit_started, layout) Nanoc::Int::NotificationCenter.post(:visit_ended, layout) begin # Notify start Nanoc::Int::NotificationCenter.post(:processing_started, layout) Nanoc::Int::NotificationCenter.post(:filtering_started, rep, filter_name) # Layout content = layout.content arg = content.binary? ? content.filename : content.string res = filter.setup_and_run(arg, filter_args) rep.snapshot_contents[:last] = Nanoc::Int::TextualContent.new(res).tap(&:freeze) # Create "post" snapshot snapshot(rep, :post, final: false) ensure # Notify end Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, filter_name) Nanoc::Int::NotificationCenter.post(:processing_ended, layout) end end def snapshot(rep, snapshot_name, final: true, path: nil) # rubocop:disable Lint/UnusedMethodArgument # NOTE: :path is irrelevant unless rep.binary? rep.snapshot_contents[snapshot_name] = rep.snapshot_contents[:last] end if snapshot_name == :pre && final rep.snapshot_defs << Nanoc::Int::SnapshotDef.new(:pre, true) end if final raw_path = rep.raw_path(snapshot: snapshot_name) if raw_path ItemRepWriter.new.write(rep, raw_path) end end end def assigns_for(rep) @compiler.assigns_for(rep) end def layouts @compiler.site.layouts end def find_layout(arg) req_id = arg.__nanoc_cleaned_identifier layout = layouts.find { |l| l.identifier == req_id } return layout if layout if use_globs? pat = Nanoc::Int::Pattern.from(arg) layout = layouts.find { |l| pat.match?(l.identifier) } return layout if layout end raise Nanoc::Int::Errors::UnknownLayout.new(arg) end def use_globs? @compiler.site.config[:string_pattern_type] == 'glob' end end end end nanoc-4.1.4/lib/nanoc/base/services/temp_filename_factory.rb0000644000004100000410000000234412665031555024143 0ustar www-datawww-datarequire 'tmpdir' module Nanoc::Int # @api private class TempFilenameFactory # @return [String] The root directory for all temporary filenames attr_reader :root_dir # @return [Nanoc::Int::TempFilenameFactory] A common instance def self.instance @instance ||= new end def initialize @counts = {} @root_dir = Dir.mktmpdir('nanoc') end # @param [String] prefix A string prefix to include in the temporary # filename, often the type of filename being provided. # # @return [String] A new unused filename def create(prefix) count = @counts.fetch(prefix, 0) @counts[prefix] = count + 1 dirname = File.join(@root_dir, prefix) filename = File.join(@root_dir, prefix, count.to_s) FileUtils.mkdir_p(dirname) filename end # @param [String] prefix A string prefix that indicates which temporary # filenames should be deleted. # # @return [void] def cleanup(prefix) path = File.join(@root_dir, prefix) if File.exist?(path) FileUtils.rm_rf(path) end @counts.delete(prefix) if @counts.empty? && File.directory?(@root_dir) FileUtils.rm_rf(@root_dir) end end end end nanoc-4.1.4/lib/nanoc/base/views.rb0000644000004100000410000000220412665031555017114 0ustar www-datawww-datarequire_relative 'views/mixins/document_view_mixin' require_relative 'views/mixins/mutable_document_view_mixin' require_relative 'views/mixins/with_reps_view_mixin' require_relative 'views/view' require_relative 'views/view_context' require_relative 'views/config_view' require_relative 'views/identifiable_collection_view' require_relative 'views/item_without_reps_view' require_relative 'views/item_with_reps_view' require_relative 'views/item_collection_with_reps_view' require_relative 'views/item_collection_without_reps_view' require_relative 'views/item_rep_view' require_relative 'views/item_rep_collection_view' require_relative 'views/layout_view' require_relative 'views/layout_collection_view' require_relative 'views/mutable_config_view' require_relative 'views/mutable_identifiable_collection_view' require_relative 'views/mutable_item_view' require_relative 'views/mutable_item_collection_view' require_relative 'views/mutable_layout_view' require_relative 'views/mutable_layout_collection_view' require_relative 'views/site_view' require_relative 'views/post_compile_item_view' require_relative 'views/post_compile_item_collection_view' nanoc-4.1.4/lib/nanoc/base/memoization.rb0000644000004100000410000000350312665031555020315 0ustar www-datawww-datamodule Nanoc::Int # Adds support for memoizing functions. # # @api private # # @since 3.2.0 module Memoization # Memoizes the method with the given name. The modified method will cache # the results of the original method, so that calling a method twice with # the same arguments will short-circuit and return the cached results # immediately. # # Memoization assumes that the current object as well as the function # arguments are immutable. Mutating the object or the arguments will not # cause memoized methods to recalculate their results. There is no way to # un-memoize a result, and calculation results will remain in memory even # if they are no longer needed. # # @example A fast fib function due to memoization # # class FibFast # # extend Nanoc::Int::Memoization # # def run(n) # if n == 0 # 0 # elsif n == 1 # 1 # else # run(n-1) + run(n-2) # end # end # memoize :run # # end # # @param [Symbol, String] method_name The name of the method to memoize # # @return [void] def memoize(method_name) # Alias original_method_name = '__nonmemoized_' + method_name.to_s alias_method original_method_name, method_name # Redefine define_method(method_name) do |*args| # Get cache @__memoization_cache ||= {} @__memoization_cache[method_name] ||= {} # Recalculate if necessary unless @__memoization_cache[method_name].key?(args) result = send(original_method_name, *args) @__memoization_cache[method_name][args] = result end # Done @__memoization_cache[method_name][args] end end end end nanoc-4.1.4/lib/nanoc/base/services.rb0000644000004100000410000000062212665031555017604 0ustar www-datawww-datarequire_relative 'services/action_provider' require_relative 'services/compiler_loader' require_relative 'services/executor' require_relative 'services/item_rep_builder' require_relative 'services/item_rep_router' require_relative 'services/item_rep_selector' require_relative 'services/item_rep_writer' require_relative 'services/notification_center' require_relative 'services/temp_filename_factory' nanoc-4.1.4/lib/nanoc/base/repos/0000755000004100000410000000000012665031555016564 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/base/repos/compiled_content_cache.rb0000644000004100000410000000262212665031555023564 0ustar www-datawww-datamodule Nanoc::Int # Represents a cache than can be used to store already compiled content, # to prevent it from being needlessly recompiled. # # @api private class CompiledContentCache < ::Nanoc::Int::Store def initialize super('tmp/compiled_content', 1) @cache = {} end # Returns the cached compiled content for the given item # representation. This cached compiled content is a hash where the keys # are the snapshot names and the values the compiled content at the # given snapshot. # # @param [Nanoc::Int::ItemRep] rep The item rep to fetch the content for # # @return [Hash] A hash containing the cached compiled # content for the given item representation def [](rep) item_cache = @cache[rep.item.identifier] || {} item_cache[rep.name] end # Sets the compiled content for the given representation. # # @param [Nanoc::Int::ItemRep] rep The item representation for which to set # the compiled content # # @param [Hash] content A hash containing the compiled # content of the given representation # # @return [void] def []=(rep, content) @cache[rep.item.identifier] ||= {} @cache[rep.item.identifier][rep.name] = content end protected def data @cache end def data=(new_data) @cache = new_data end end end nanoc-4.1.4/lib/nanoc/base/repos/site_loader.rb0000644000004100000410000000454412665031555021412 0ustar www-datawww-datamodule Nanoc::Int class SiteLoader def new_empty site_from_config(Nanoc::Int::Configuration.new.with_defaults) end def new_with_config(hash) site_from_config(Nanoc::Int::Configuration.new(hash).with_defaults) end def new_from_cwd site_from_config(Nanoc::Int::ConfigLoader.new.new_from_cwd) end # @return [Boolean] def self.cwd_is_nanoc_site? Nanoc::Int::ConfigLoader.cwd_is_nanoc_site? end private def site_from_config(config) code_snippets = code_snippets_from_config(config) code_snippets.each(&:load) items = Nanoc::Int::IdentifiableCollection.new(config) layouts = Nanoc::Int::IdentifiableCollection.new(config) with_data_sources(config) do |data_sources| data_sources.each do |ds| items_in_ds = ds.items layouts_in_ds = ds.layouts items_in_ds.each { |i| i.identifier = i.identifier.prefix(ds.items_root) } layouts_in_ds.each { |l| l.identifier = l.identifier.prefix(ds.layouts_root) } items.concat(items_in_ds) layouts.concat(layouts_in_ds) end end Nanoc::Int::Site.new( config: config, code_snippets: code_snippets, items: items, layouts: layouts, ) end def with_data_sources(config, &_block) data_sources = create_data_sources(config) begin data_sources.each(&:use) yield(data_sources) ensure data_sources.each(&:unuse) end end def create_data_sources(config) config[:data_sources].map do |data_source_hash| # Get data source class data_source_class = Nanoc::DataSource.named(data_source_hash[:type]) if data_source_class.nil? raise Nanoc::Int::Errors::UnknownDataSource.new(data_source_hash[:type]) end # Create data source data_source_class.new( config, data_source_hash[:items_root], data_source_hash[:layouts_root], data_source_hash.merge(data_source_hash[:config] || {}), ) end end def code_snippets_from_config(config) config[:lib_dirs].flat_map do |lib| Dir["#{lib}/**/*.rb"].sort.map do |filename| Nanoc::Int::CodeSnippet.new( File.read(filename), filename, ) end end end end end nanoc-4.1.4/lib/nanoc/base/repos/checksum_store.rb0000644000004100000410000000203212665031555022124 0ustar www-datawww-datamodule Nanoc::Int # Stores checksums for objects in order to be able to detect whether a file # has changed since the last site compilation. # # @api private class ChecksumStore < ::Nanoc::Int::Store # @param [Nanoc::Int::Site] site def initialize(site: nil) super('tmp/checksums', 1) @site = site @checksums = {} end # Returns the old checksum for the given object. This makes sense for # items, layouts and code snippets. # # @param [#reference] obj The object for which to fetch the checksum # # @return [String] The checksum for the given object def [](obj) @checksums[obj.reference] end # Sets the checksum for the given object. # # @param [#reference] obj The object for which to set the checksum # # @param [String] checksum The checksum def []=(obj, checksum) @checksums[obj.reference] = checksum end protected def data @checksums end def data=(new_data) @checksums = new_data end end end nanoc-4.1.4/lib/nanoc/base/repos/data_source.rb0000644000004100000410000001334212665031555021405 0ustar www-datawww-datamodule Nanoc # Responsible for loading site data. It is the (abstract) superclass for all # data sources. Subclasses must at least implement the data reading methods # ({#items} and {#layouts}). # # Apart from the methods for loading and storing data, there are the {#up} # and {#down} methods for bringing up and tearing down the connection to the # data source. These should be overridden in subclasses. The {#loading} # method wraps {#up} and {#down}. {#loading} is a convenience method for the # more low-level methods {#use} and {#unuse}, which respectively increment # and decrement the reference count; when the reference count goes from 0 to # 1, the data source will be loaded ({#up} will be called) and when the # reference count goes from 1 to 0, the data source will be unloaded # ({#down} will be called). # # @abstract Subclasses should at least implement {#items} and {#layouts}. class DataSource # @return [String] The root path where items returned by this data source # should be mounted. attr_reader :items_root # @return [String] The root path where layouts returned by this data # source should be mounted. attr_reader :layouts_root # @return [Hash] The configuration for this data source. For example, # online data sources could contain authentication details. attr_reader :config extend Nanoc::Int::PluginRegistry::PluginMethods def initialize(site_config, items_root, layouts_root, config) @site_config = site_config @items_root = items_root @layouts_root = layouts_root @config = config || {} @references = 0 end # Loads the data source when necessary (calling {#up}), yields, and # unloads (using {#down}) the data source when it is not being used # elsewhere. All data source queries and data manipulations should be # wrapped in a {#loading} block; it ensures that the data source is loaded # when necessary and makes sure the data source does not get unloaded # while it is still being used elsewhere. # # @return [void] def loading use yield ensure unuse end # Marks the data source as used by the caller. # # Calling this method increases the internal reference count. When the # data source is used for the first time (first {#use} call), the data # source will be loaded ({#up} will be called). # # @return [void] def use up if @references == 0 @references += 1 end # Marks the data source as unused by the caller. # # Calling this method decreases the internal reference count. When the # reference count reaches zero, i.e. when the data source is not used any # more, the data source will be unloaded ({#down} will be called). # # @return [void] def unuse @references -= 1 down if @references == 0 end # Brings up the connection to the data. Depending on the way data is # stored, this may not be necessary. This is the ideal place to connect to # the database, for example. # # Subclasses may override this method, but are not required to do so; the # default implementation simply does nothing. # # @return [void] def up end # Brings down the connection to the data. This method should undo the # effects of the {#up} method. For example, a database connection # established in {#up} should be closed in this method. # # Subclasses may override this method, but are not required to do so; the # default implementation simply does nothing. # # @return [void] def down end # Returns the collection of items (represented by {Nanoc::Int::Item}) in # this site. The default implementation simply returns an empty array. # # Subclasses should not prepend `items_root` to the item's identifiers, as # this will be done automatically. # # Subclasses may override this method, but are not required to do so; the # default implementation simply does nothing. # # @return [Enumerable] The collection of items def items [] end # Returns the collection of layouts (represented by {Nanoc::Int::Layout}) in # this site. The default implementation simply returns an empty array. # # Subclasses should prepend `layout_root` to the layout's identifiers, # since this is not done automatically. # # Subclasses may override this method, but are not required to do so; the # default implementation simply does nothing. # # @return [Enumerable] The collection of layouts def layouts [] end # Creates a new in-memory item instance. This is intended for use within # the {#items} method. # # @param [String] content The uncompiled item content # (if it is a textual item) or the path to the filename containing the # content (if it is a binary item). # # @param [Hash] attributes A hash containing this item's attributes. # # @param [String] identifier This item's identifier. # # @param [Boolean] binary Whether or not this item is binary def new_item(content, attributes, identifier, binary: false) content = Nanoc::Int::Content.create(content, binary: binary) Nanoc::Int::Item.new(content, attributes, identifier) end # Creates a new in-memory layout instance. This is intended for use within # the {#layouts} method. # # @param [String] raw_content The raw content of this layout. # # @param [Hash] attributes A hash containing this layout's attributes. # # @param [String] identifier This layout's identifier. def new_layout(raw_content, attributes, identifier) Nanoc::Int::Layout.new(raw_content, attributes, identifier) end end end nanoc-4.1.4/lib/nanoc/base/repos/rule_memory_store.rb0000644000004100000410000000204512665031555022665 0ustar www-datawww-datamodule Nanoc::Int # Stores rule memories for objects that can be run through a rule (item # representations and layouts). # # @api private class RuleMemoryStore < ::Nanoc::Int::Store def initialize super('tmp/rule_memory', 1) @rule_memories = {} end # @param [Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The item representation or # the layout to get the rule memory for # # @return [Array] The rule memory for the given object def [](obj) @rule_memories[obj.reference] end # @param [Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The item representation or # the layout to set the rule memory for # # @param [Array] rule_memory The new rule memory to be stored # # @return [void] def []=(obj, rule_memory) @rule_memories[obj.reference] = rule_memory end protected # @see Nanoc::Int::Store#data def data @rule_memories end # @see Nanoc::Int::Store#data= def data=(new_data) @rule_memories = new_data end end end nanoc-4.1.4/lib/nanoc/base/repos/dependency_store.rb0000644000004100000410000001010112665031555022434 0ustar www-datawww-datamodule Nanoc::Int # @api private class DependencyStore < ::Nanoc::Int::Store # @return [Array] attr_reader :objects # @param [Array] objects def initialize(objects) super('tmp/dependencies', 4) @objects = objects @graph = Nanoc::Int::DirectedGraph.new([nil] + @objects) end # Returns the direct dependencies for the given object. # # The direct dependencies of the given object include the items and # layouts that, when outdated will cause the given object to be marked as # outdated. Indirect dependencies will not be returned (e.g. if A depends # on B which depends on C, then the direct dependencies of A do not # include C). # # The direct predecessors can include nil, which indicates an item that is # no longer present in the site. # # @param [Nanoc::Int::Item, Nanoc::Int::Layout] object The object for # which to fetch the direct predecessors # # @return [Array] The direct # predecessors of # the given object def objects_causing_outdatedness_of(object) @graph.direct_predecessors_of(object) end # Returns the direct inverse dependencies for the given object. # # The direct inverse dependencies of the given object include the objects # that will be marked as outdated when the given object is outdated. # Indirect dependencies will not be returned (e.g. if A depends on B which # depends on C, then the direct inverse dependencies of C do not include # A). # # @param [Nanoc::Int::Item, Nanoc::Int::Layout] object The object for which to # fetch the direct successors # # @return [Array] The direct successors of # the given object def objects_outdated_due_to(object) @graph.direct_successors_of(object).compact end # Records a dependency from `src` to `dst` in the dependency graph. When # `dst` is oudated, `src` will also become outdated. # # @param [Nanoc::Int::Item, Nanoc::Int::Layout] src The source of the dependency, # i.e. the object that will become outdated if dst is outdated # # @param [Nanoc::Int::Item, Nanoc::Int::Layout] dst The destination of the # dependency, i.e. the object that will cause the source to become # outdated if the destination is outdated # # @return [void] def record_dependency(src, dst) # Warning! dst and src are *reversed* here! @graph.add_edge(dst, src) unless src == dst end # Empties the list of dependencies for the given object. This is necessary # before recompiling the given object, because otherwise old dependencies # will stick around and new dependencies will appear twice. This function # removes all incoming edges for the given vertex. # # @param [Nanoc::Int::Item, Nanoc::Int::Layout] object The object for which to # forget all dependencies # # @return [void] def forget_dependencies_for(object) @graph.delete_edges_to(object) end protected def data { edges: @graph.edges, vertices: @graph.vertices.map { |obj| obj && obj.reference }, } end def data=(new_data) # Create new graph @graph = Nanoc::Int::DirectedGraph.new([nil] + @objects) # Load vertices previous_objects = new_data[:vertices].map do |reference| @objects.find { |obj| reference == obj.reference } end # Load edges new_data[:edges].each do |edge| from_index, to_index = *edge from = from_index && previous_objects[from_index] to = to_index && previous_objects[to_index] @graph.add_edge(from, to) end # Record dependency from all items on new items new_objects = (@objects - previous_objects) new_objects.each do |new_obj| @objects.each do |obj| next unless obj.is_a?(Nanoc::Int::Item) @graph.add_edge(new_obj, obj) end end end end end nanoc-4.1.4/lib/nanoc/base/repos/store.rb0000644000004100000410000000672012665031555020252 0ustar www-datawww-datamodule Nanoc::Int # An abstract superclass for classes that need to store data to the # filesystem, such as checksums, cached compiled content and dependency # graphs. # # Each store has a version number. When attempting to load data from a store # that has an incompatible version number, no data will be loaded, but # {#version_mismatch_detected} will be called. # # @abstract Subclasses must implement {#data} and {#data=}, and may # implement {#no_data_found} and {#version_mismatch_detected}. # # @api private class Store # @return [String] The name of the file where data will be loaded from and # stored to. attr_reader :filename # @return [Numeric] The version number corresponding to the file format # the data is in. When the file format changes, the version number # should be incremented. attr_reader :version # Creates a new store for the given filename. # # @param [String] filename The name of the file where data will be loaded # from and stored to. # # @param [Numeric] version The version number corresponding to the file # format the data is in. When the file format changes, the version # number should be incremented. def initialize(filename, version) @filename = filename @version = version end # @group Loading and storing data # @return The data that should be written to the disk # # @abstract This method must be implemented by the subclass. def data raise NotImplementedError.new('Nanoc::Int::Store subclasses must implement #data and #data=') end # @param new_data The data that has been loaded from the disk # # @abstract This method must be implemented by the subclass. # # @return [void] def data=(new_data) # rubocop:disable Lint/UnusedMethodArgument raise NotImplementedError.new('Nanoc::Int::Store subclasses must implement #data and #data=') end # Loads the data from the filesystem into memory. This method will set the # loaded data using the {#data=} method. # # @return [void] def load # Check file existance unless File.file?(filename) no_data_found return end begin pstore.transaction do # Check version if pstore[:version] != version version_mismatch_detected return end # Load self.data = pstore[:data] end rescue FileUtils.rm_f(filename) load end end # Stores the data contained in memory to the filesystem. This method will # use the {#data} method to fetch the data that should be written. # # @return [void] def store FileUtils.mkdir_p(File.dirname(filename)) pstore.transaction do pstore[:data] = data pstore[:version] = version end end # @group Callback methods # Callback method that is called when no data file was found. By default, # this implementation does nothing, but it should probably be overridden # by the subclass. # # @return [void] def no_data_found end # Callback method that is called when a version mismatch is detected. By # default, this implementation does nothing, but it should probably be # overridden by the subclass. # # @return [void] def version_mismatch_detected end private def pstore @pstore ||= PStore.new(filename) end end end nanoc-4.1.4/lib/nanoc/base/repos/config_loader.rb0000644000004100000410000000367212665031555021714 0ustar www-datawww-datamodule Nanoc::Int # @api private class ConfigLoader class NoConfigFileFoundError < ::Nanoc::Error def initialize super('No configuration file found') end end class NoParentConfigFileFoundError < ::Nanoc::Error def initialize(filename) super("There is no parent configuration file at #{filename}") end end class CyclicalConfigFileError < ::Nanoc::Error def initialize(filename) super("The parent configuration file at #{filename} includes one of its descendants") end end # @return [Boolean] def self.cwd_is_nanoc_site? !config_filename_for_cwd.nil? end # @return [String] def self.config_filename_for_cwd filenames = %w( nanoc.yaml config.yaml ) candidate = filenames.find { |f| File.file?(f) } candidate && File.expand_path(candidate) end def new_from_cwd # Determine path filename = self.class.config_filename_for_cwd raise NoConfigFileFoundError if filename.nil? # Read apply_parent_config( Nanoc::Int::Configuration.new(YAML.load_file(filename)), [filename]).with_defaults end # @api private def apply_parent_config(config, processed_paths = []) parent_path = config[:parent_config_file] return config if parent_path.nil? # Get absolute path parent_path = File.absolute_path(parent_path, File.dirname(processed_paths.last)) unless File.file?(parent_path) raise NoParentConfigFileFoundError.new(parent_path) end # Check recursion if processed_paths.include?(parent_path) raise CyclicalConfigFileError.new(parent_path) end # Load parent_config = Nanoc::Int::Configuration.new(YAML.load_file(parent_path)) full_parent_config = apply_parent_config(parent_config, processed_paths + [parent_path]) full_parent_config.merge(config.without(:parent_config_file)) end end end nanoc-4.1.4/lib/nanoc/base/entities.rb0000644000004100000410000000112512665031555017604 0ustar www-datawww-datarequire_relative 'entities/code_snippet' require_relative 'entities/configuration' require_relative 'entities/content' require_relative 'entities/document' require_relative 'entities/identifier' require_relative 'entities/identifiable_collection' require_relative 'entities/item' require_relative 'entities/item_rep' require_relative 'entities/layout' require_relative 'entities/pattern' require_relative 'entities/rule_memory' require_relative 'entities/rule_memory_action' require_relative 'entities/rule_memory_actions' require_relative 'entities/site' require_relative 'entities/snapshot_def' nanoc-4.1.4/lib/nanoc/base/directed_graph.rb0000644000004100000410000001532712665031555020735 0ustar www-datawww-datamodule Nanoc::Int # Represents a directed graph. It is used by the dependency tracker for # storing and querying dependencies between items. # # @example Creating and using a directed graph # # # Create a graph with three vertices # graph = Nanoc::Int::DirectedGraph.new(%w( a b c d )) # # # Add edges # graph.add_edge('a', 'b') # graph.add_edge('b', 'c') # graph.add_edge('c', 'd') # # # Get (direct) predecessors # graph.direct_predecessors_of('d').sort # # => %w( c ) # graph.predecessors_of('d').sort # # => %w( a b c ) # # # Modify edges # graph.delete_edge('a', 'b') # # # Get (direct) predecessors again # graph.direct_predecessors_of('d').sort # # => %w( c ) # graph.predecessors_of('d').sort # # => %w( b c ) # # @api private class DirectedGraph # @group Creating a graph # Creates a new directed graph with the given vertices. def initialize(vertices) @vertices = {} vertices.each_with_index do |v, i| @vertices[v] = i end @from_graph = {} @to_graph = {} @roots = Set.new(@vertices.keys) invalidate_caches end # @group Modifying the graph # Adds an edge from the first vertex to the second vertex. # # @param from Vertex where the edge should start # # @param to Vertex where the edge should end # # @return [void] def add_edge(from, to) add_vertex(from) add_vertex(to) @from_graph[from] ||= Set.new @from_graph[from] << to @to_graph[to] ||= Set.new @to_graph[to] << from @roots.delete(to) invalidate_caches end # Removes the edge from the first vertex to the second vertex. If the # edge does not exist, nothing is done. # # @param from Start vertex of the edge # # @param to End vertex of the edge # # @return [void] # # @since 3.2.0 def delete_edge(from, to) @from_graph[from] ||= Set.new @from_graph[from].delete(to) @to_graph[to] ||= Set.new @to_graph[to].delete(from) @roots.add(to) if @to_graph[to].empty? invalidate_caches end # Adds the given vertex to the graph. # # @param v The vertex to add to the graph # # @return [void] # # @since 3.2.0 def add_vertex(v) return if @vertices.key?(v) @vertices[v] = @vertices.size @roots << v end # Deletes all edges coming from the given vertex. # # @param from Vertex from which all edges should be removed # # @return [void] # # @since 3.2.0 def delete_edges_from(from) return if @from_graph[from].nil? @from_graph[from].each do |to| @to_graph[to].delete(from) @roots.add(to) if @to_graph[to].empty? end @from_graph.delete(from) end # Deletes all edges going to the given vertex. # # @param to Vertex to which all edges should be removed # # @return [void] def delete_edges_to(to) return if @to_graph[to].nil? @to_graph[to].each do |from| @from_graph[from].delete(to) end @to_graph.delete(to) @roots.add(to) end # Removes the given vertex from the graph. # # @param v Vertex to remove from the graph # # @return [void] # # @since 3.2.0 def delete_vertex(v) delete_edges_to(v) delete_edges_from(v) @vertices.delete(v) @roots.delete(v) end # @group Querying the graph # Returns the direct predecessors of the given vertex, i.e. the vertices # x where there is an edge from x to the given vertex y. # # @param to The vertex of which the predecessors should be calculated # # @return [Array] Direct predecessors of the given vertex def direct_predecessors_of(to) @to_graph[to].to_a end # Returns the direct successors of the given vertex, i.e. the vertices y # where there is an edge from the given vertex x to y. # # @param from The vertex of which the successors should be calculated # # @return [Array] Direct successors of the given vertex def direct_successors_of(from) @from_graph[from].to_a end # Returns the predecessors of the given vertex, i.e. the vertices x for # which there is a path from x to the given vertex y. # # @param to The vertex of which the predecessors should be calculated # # @return [Array] Predecessors of the given vertex def predecessors_of(to) @predecessors[to] ||= recursively_find_vertices(to, :direct_predecessors_of) end # Returns the successors of the given vertex, i.e. the vertices y for # which there is a path from the given vertex x to y. # # @param from The vertex of which the successors should be calculated # # @return [Array] Successors of the given vertex def successors_of(from) @successors[from] ||= recursively_find_vertices(from, :direct_successors_of) end # @return [Array] The list of all vertices in this graph. def vertices @vertices.keys.sort_by { |v| @vertices[v] } end # Returns an array of tuples representing the edges. The result of this # method may take a while to compute and should be cached if possible. # # @return [Array] The list of all edges in this graph. def edges result = [] @vertices.each_pair do |v1, i1| direct_successors_of(v1).map { |v2| @vertices[v2] }.each do |i2| result << [i1, i2] end end result end # Returns all root vertices, i.e. vertices where no edge points to. # # @return [Set] The set of all root vertices in this graph. # # @since 3.2.0 def roots @roots end private # Invalidates cached data. This method should be called when the internal # graph representation is changed. def invalidate_caches @predecessors = {} @successors = {} end # Recursively finds vertices, starting at the vertex start, using the # given method, which should be a symbol to a method that takes a vertex # and returns related vertices (e.g. predecessors, successors). def recursively_find_vertices(start, method) all_vertices = Set.new processed_vertices = Set.new unprocessed_vertices = [start] until unprocessed_vertices.empty? # Get next unprocessed vertex vertex = unprocessed_vertices.pop next if processed_vertices.include?(vertex) processed_vertices << vertex # Add predecessors of this vertex send(method, vertex).each do |v| all_vertices << v unless all_vertices.include?(v) unprocessed_vertices << v end end all_vertices.to_a end end end nanoc-4.1.4/lib/nanoc/base/checksummer.rb0000644000004100000410000000603312665031555020271 0ustar www-datawww-datamodule Nanoc::Int # Creates checksums for given objects. # # A checksum is a string, such as “mL+TaqNsEeiPkWloPgCtAofT1yg=”, that is used # to determine whether a piece of data has changed. # # @api private class Checksummer class VerboseDigest def initialize @str = '' end def update(str) @str << str end def to_s @str end end class CompactDigest def initialize @digest = Digest::SHA1.new end def update(str) @digest.update(str) end def to_s @digest.base64digest end end class << self # @param obj The object to create a checksum for # # @return [String] The digest def calc(obj, digest_class = CompactDigest) digest = digest_class.new update(obj, digest) digest.to_s end private def update(obj, digest, visited = Set.new) digest.update(obj.class.to_s) digest.update('<') if visited.include?(obj) digest.update('recur>') return end case obj when ::String, ::Symbol, ::Numeric digest.update(obj.to_s) when nil, true, false when ::Array, ::Nanoc::Int::IdentifiableCollection obj.each do |el| update(el, digest, visited + [obj]) digest.update(',') end when ::Hash, ::Nanoc::Int::Configuration obj.each do |key, value| update(key, digest, visited + [obj]) digest.update('=') update(value, digest, visited + [obj]) digest.update(',') end when ::Pathname filename = obj.to_s if File.exist?(filename) stat = File.stat(filename) digest.update(stat.size.to_s + '-' + stat.mtime.to_i.to_s) else digest.update('???') end when Time digest.update(obj.to_i.to_s) when Nanoc::Identifier update(obj.to_s, digest) # TODO: Use RuleMemory rather than RulesCollection when Nanoc::RuleDSL::RulesCollection, Nanoc::Int::CodeSnippet update(obj.data, digest) when Nanoc::Int::TextualContent update(obj.string, digest) when Nanoc::Int::BinaryContent update(Pathname.new(obj.filename), digest) when Nanoc::Int::Item, Nanoc::Int::Layout digest.update('content=') update(obj.content, digest) digest.update(',attributes=') update(obj.attributes, digest, visited + [obj]) digest.update(',identifier=') update(obj.identifier, digest) when Nanoc::ItemWithRepsView, Nanoc::ItemWithoutRepsView, Nanoc::LayoutView, Nanoc::ConfigView, Nanoc::IdentifiableCollectionView update(obj.unwrap, digest) else data = begin Marshal.dump(obj) rescue obj.inspect end digest.update(data) end digest.update('>') end end end end nanoc-4.1.4/lib/nanoc/base/error.rb0000644000004100000410000000016412665031555017113 0ustar www-datawww-datamodule Nanoc # Generic error. Superclass for all Nanoc-specific errors. class Error < ::StandardError end end nanoc-4.1.4/lib/nanoc/filters/0000755000004100000410000000000012665031555016172 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/filters/markaby.rb0000644000004100000410000000067612665031555020156 0ustar www-datawww-datamodule Nanoc::Filters # @api private class Markaby < Nanoc::Filter requires 'markaby' # Runs the content through [Markaby](http://markaby.github.io/). # This method takes no options. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, _params = {}) # Get result ::Markaby::Builder.new(assigns).instance_eval(content).to_s end end end nanoc-4.1.4/lib/nanoc/filters/redcarpet.rb0000644000004100000410000000531012665031555020467 0ustar www-datawww-datamodule Nanoc::Filters # @since 3.2.0 # # @api private class Redcarpet < Nanoc::Filter requires 'redcarpet' # Runs the content through [Redcarpet](https://github.com/vmg/redcarpet). # This method optionally takes processing options to pass on to Redcarpet. # # @overload run(content, params={}) # # For Redcarpet 1.x # # @param [String] content The content to filter # # @option params [Array] :options ([]) A list of options to pass on to # Redcarpet # # @return [String] The filtered content # # @overload run(content, params={}) # # For Redcarpet 2.x # # @since 3.2.4 # # @param [String] content The content to filter # # @option params [Hash] :options ({}) A list of options to pass on to # Redcarpet itself (not the renderer) # # @option params [::Redcarpet::Render::Base] :renderer # (::Redcarpet::Render::HTML) The class of the renderer to use # # @option params [Hash] :renderer_options ({}) A list of options to pass # on to the Redcarpet renderer # # @option params [Boolean] :with_toc (false) A boolean to request a table # of contents # # @return [String] The filtered content def run(content, params = {}) if ::Redcarpet::VERSION > '2' options = params.fetch(:options, {}) renderer_class = params.fetch(:renderer, ::Redcarpet::Render::HTML) renderer_options = params.fetch(:renderer_options, {}) with_toc = params.fetch(:with_toc, false) if options.is_a?(Array) warn 'WARNING: You are passing an array of options to the :redcarpet filter, but Redcarpet 2.x expects a hash instead. This will likely fail.' end # Setup TOC if with_toc unless renderer_class <= ::Redcarpet::Render::HTML raise "Unexpected renderer: #{renderer_class}" end # `with_toc` implies `with_toc_data` for the HTML renderer renderer_options[:with_toc_data] = true end # Create renderer renderer = if renderer_class == ::Redcarpet::Render::HTML_TOC renderer_class.new else renderer_class.new(renderer_options) end # Render if with_toc renderer_toc = ::Redcarpet::Render::HTML_TOC.new toc = ::Redcarpet::Markdown.new(renderer_toc, options).render(content) body = ::Redcarpet::Markdown.new(renderer, options).render(content) toc + body else ::Redcarpet::Markdown.new(renderer, options).render(content) end end end end end nanoc-4.1.4/lib/nanoc/filters/rdiscount.rb0000644000004100000410000000101212665031555020523 0ustar www-datawww-datamodule Nanoc::Filters # @api private class RDiscount < Nanoc::Filter requires 'rdiscount' # Runs the content through [RDiscount](http://github.com/rtomayko/rdiscount). # # @option params [Array] :extensions ([]) A list of RDiscount extensions # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, params = {}) extensions = params[:extensions] || [] ::RDiscount.new(content, *extensions).to_html end end end nanoc-4.1.4/lib/nanoc/filters/less.rb0000644000004100000410000000314312665031555017466 0ustar www-datawww-datamodule Nanoc::Filters # @api private class Less < Nanoc::Filter requires 'less' # Runs the content through [LESS](http://lesscss.org/). # This method takes no options. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, params = {}) # Find imports (hacky) imports = [] imports.concat(content.scan(/^@import\s+(["'])([^\1]+?)\1;/)) imports.concat(content.scan(/^@import\s+url\((["']?)([^)]+?)\1\);/)) imported_filenames = imports.map do |i| i[1] =~ /\.(less|css)$/ ? i[1] : i[1] + '.less' end # Convert to items imported_items = imported_filenames.map do |filename| # Find directory for this item current_dir_pathname = Pathname.new(@item[:content_filename]).dirname.realpath # Find absolute pathname for imported item imported_pathname = Pathname.new(filename) if imported_pathname.relative? imported_pathname = current_dir_pathname + imported_pathname end next unless imported_pathname.exist? imported_filename = imported_pathname.realpath # Find matching item @items.find do |i| next if i[:content_filename].nil? Pathname.new(i[:content_filename]).realpath == imported_filename end end.compact # Create dependencies depend_on(imported_items) # Add filename to load path paths = [File.dirname(@item[:content_filename])] parser = ::Less::Parser.new(paths: paths) parser.parse(content).to_css params end end end nanoc-4.1.4/lib/nanoc/filters/bluecloth.rb0000644000004100000410000000064312665031555020503 0ustar www-datawww-datamodule Nanoc::Filters # @api private class BlueCloth < Nanoc::Filter requires 'bluecloth' # Runs the content through [BlueCloth](http://deveiate.org/projects/BlueCloth). # This method takes no options. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, _params = {}) ::BlueCloth.new(content).to_html end end end nanoc-4.1.4/lib/nanoc/filters/coffeescript.rb0000644000004100000410000000072212665031555021174 0ustar www-datawww-datamodule Nanoc::Filters # @since 3.3.0 # # @api private class CoffeeScript < Nanoc::Filter requires 'coffee-script' # Runs the content through [CoffeeScript](http://coffeescript.org/). # This method takes no options. # # @param [String] content The CoffeeScript content to turn into JavaScript # # @return [String] The resulting JavaScript def run(content, _params = {}) ::CoffeeScript.compile(content) end end end nanoc-4.1.4/lib/nanoc/filters/redcloth.rb0000644000004100000410000000306012665031555020322 0ustar www-datawww-datamodule Nanoc::Filters # @api private class RedCloth < Nanoc::Filter requires 'redcloth' # Runs the content through [RedCloth](http://redcloth.org/). This method # takes the following options: # # * `:filter_class` # * `:filter_html` # * `:filter_ids` # * `:filter_style` # * `:hard_breaks` # * `:lite_mode` # * `:no_span_caps` # * `:sanitize_htm` # # Each of these options sets the corresponding attribute on the `RedCloth` # instance. For example, when the `:hard_breaks => false` option is passed # to this filter, the filter will call `r.hard_breaks = false` (with `r` # being the `RedCloth` instance). # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, params = {}) # Create formatter r = ::RedCloth.new(content) # Set options r.filter_classes = params[:filter_classes] if params.key?(:filter_classes) r.filter_html = params[:filter_html] if params.key?(:filter_html) r.filter_ids = params[:filter_ids] if params.key?(:filter_ids) r.filter_styles = params[:filter_styles] if params.key?(:filter_styles) r.hard_breaks = params[:hard_breaks] if params.key?(:hard_breaks) r.lite_mode = params[:lite_mode] if params.key?(:lite_mode) r.no_span_caps = params[:no_span_caps] if params.key?(:no_span_caps) r.sanitize_html = params[:sanitize_html] if params.key?(:sanitize_html) # Get result r.to_html end end end nanoc-4.1.4/lib/nanoc/filters/sass.rb0000644000004100000410000000221712665031555017472 0ustar www-datawww-datamodule Nanoc::Filters # @api private class Sass < Nanoc::Filter requires 'sass', 'nanoc/filters/sass/sass_filesystem_importer' # Runs the content through [Sass](http://sass-lang.com/). # Parameters passed to this filter will be passed on to Sass. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, params = {}) options = params.merge( nanoc_current_filter: self, filename: @item && @item.raw_filename, ) engine = ::Sass::Engine.new(content, options) engine.render end def self.item_filename_map_for_site(site, items) @item_filename_map ||= {} @item_filename_map[site] ||= {}.tap do |map| items.each do |item| if item.raw_filename path = Pathname.new(item.raw_filename).realpath.to_s map[path] = item end end end end def imported_filename_to_item(filename) realpath = Pathname.new(filename).realpath.to_s map = self.class.item_filename_map_for_site(@site, @items) map[realpath] end end end nanoc-4.1.4/lib/nanoc/filters/xsl.rb0000644000004100000410000000300112665031555017317 0ustar www-datawww-datamodule Nanoc::Filters # @since 3.3.0 # # @api private class XSL < Nanoc::Filter requires 'nokogiri' # Runs the item content through an [XSLT](http://www.w3.org/TR/xslt) # stylesheet using [Nokogiri](http://nokogiri.org/). # # This filter can only be run for layouts, because it will need both the # XML to convert (= the item content) as well as the XSLT stylesheet (= # the layout content). # # Additional parameters can be passed to the layout call. These parameters # will be turned into `xsl:param` elements. # # @example Invoking the filter as a layout # # compile '/reports/*/' do # layout 'xsl-report' # end # # layout 'xsl-report', :xsl, :awesome => 'definitely' # # @param [String] _content Ignored. As the filter can be run only as a # layout, the value of the `:content` parameter passed to the class at # initialization is used as the content to transform. # # @param [Hash] params The parameters that will be stored in corresponding # `xsl:param` elements. # # @return [String] The transformed content def run(_content, params = {}) Nanoc::Extra::JRubyNokogiriWarner.check_and_warn if assigns[:layout].nil? raise 'The XSL filter can only be run as a layout' end xml = ::Nokogiri::XML(assigns[:content]) xsl = ::Nokogiri::XSLT(assigns[:layout].raw_content) xsl.apply_to(xml, ::Nokogiri::XSLT.quote_params(params)) end end end nanoc-4.1.4/lib/nanoc/filters/mustache.rb0000644000004100000410000000077612665031555020342 0ustar www-datawww-datamodule Nanoc::Filters # @since 3.2.0 # # @api private class Mustache < Nanoc::Filter requires 'mustache' # Runs the content through # [Mustache](http://github.com/defunkt/mustache). This method takes no # options. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, _params = {}) context = item.attributes.merge({ yield: assigns[:content] }) ::Mustache.render(content, context) end end end nanoc-4.1.4/lib/nanoc/filters/handlebars.rb0000644000004100000410000000152612665031555020626 0ustar www-datawww-datamodule Nanoc::Filters # @since 3.4.0 # # @api private class Handlebars < Nanoc::Filter requires 'handlebars' # Runs the content through # [Handlebars](http://handlebarsjs.com/) using # [Handlebars.rb](https://github.com/cowboyd/handlebars.rb). # This method takes no options. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, _params = {}) context = item.attributes.dup context[:item] = assigns[:item].attributes context[:config] = assigns[:config] context[:yield] = assigns[:content] if assigns.key?(:layout) context[:layout] = assigns[:layout].attributes end handlebars = ::Handlebars::Context.new template = handlebars.compile(content) template.call(context) end end end nanoc-4.1.4/lib/nanoc/filters/typogruby.rb0000644000004100000410000000070512665031555020565 0ustar www-datawww-datamodule Nanoc::Filters # @since 3.2.0 # # @api private class Typogruby < Nanoc::Filter requires 'typogruby' # Runs the content through [Typogruby](http://avdgaag.github.com/typogruby/). # This method takes no options. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, _params = {}) # Get result ::Typogruby.improve(content) end end end nanoc-4.1.4/lib/nanoc/filters/relativize_paths.rb0000644000004100000410000001020712665031555022074 0ustar www-datawww-datamodule Nanoc::Filters # @api private class RelativizePaths < Nanoc::Filter require 'nanoc/helpers/link_to' include Nanoc::Helpers::LinkTo SELECTORS = ['*/@href', '*/@src', 'object/@data', 'param[@name="movie"]/@content', 'comment()'].freeze # Relativizes all paths in the given content, which can be HTML, XHTML, XML # or CSS. This filter is quite useful if a site needs to be hosted in a # subdirectory instead of a subdomain. In HTML, all `href` and `src` # attributes will be relativized. In CSS, all `url()` references will be # relativized. # # @param [String] content The content to filter # # @option params [Symbol] :type The type of content to filter; can be # `:html`, `:xhtml`, `:xml` or `:css`. # # @option params [Array] :select The XPath expressions that matches the # nodes to modify. This param is useful only for the `:html`, `:xml` and # `:xhtml` types. # # @option params [Hash] :namespaces The pairs `prefix => uri` to define # any namespace you want to use in the XPath expressions. This param # is useful only for the `:xml` and `:xhtml` types. # # @return [String] The filtered content def run(content, params = {}) Nanoc::Extra::JRubyNokogiriWarner.check_and_warn # Set assigns so helper function can be used @item_rep = assigns[:item_rep] if @item_rep.nil? # Filter case params[:type] when :css relativize_css(content) when :html, :xml, :xhtml relativize_html_like(content, params) else raise RuntimeError.new( 'The relativize_paths needs to know the type of content to ' \ 'process. Pass a :type to the filter call (:html for HTML, ' \ ':xhtml for XHTML, :xml for XML, or :css for CSS).') end end protected def relativize_css(content) # FIXME: parse CSS the proper way using csspool or something content.gsub(/url\((['"]?)(\/(?:[^\/].*?)?)\1\)/) do quote = Regexp.last_match[1] path = Regexp.last_match[2] 'url(' + quote + relative_path_to(path) + quote + ')' end end def relativize_html_like(content, params) selectors = params.fetch(:select, SELECTORS) namespaces = params.fetch(:namespaces, {}) type = params.fetch(:type) require 'nokogiri' case type when :html klass = ::Nokogiri::HTML when :xml klass = ::Nokogiri::XML when :xhtml klass = ::Nokogiri::XML # FIXME: cleanup because it is ugly # this cleans the XHTML namespace to process fragments and full # documents in the same way. At least, Nokogiri adds this namespace # if detects the `html` element. content = content.sub(%r{(]+)xmlns="http://www.w3.org/1999/xhtml"}, '\1') end nokogiri_process(content, selectors, namespaces, klass, type) end def nokogiri_process(content, selectors, namespaces, klass, type) # Ensure that all prefixes are strings namespaces = namespaces.reduce({}) { |new, (prefix, uri)| new.merge(prefix.to_s => uri) } doc = content =~ /]/ ? klass.parse(content) : klass.fragment(content) selectors.map { |sel| "descendant-or-self::#{sel}" }.each do |selector| doc.xpath(selector, namespaces).each do |node| if node.name == 'comment' nokogiri_process_comment(node, doc, selectors, namespaces, klass, type) elsif path_is_relativizable?(node.content) node.content = relative_path_to(node.content) end end end doc.send("to_#{type}") end def nokogiri_process_comment(node, doc, selectors, namespaces, klass, type) content = node.content.dup.sub(%r{^(\s*\[.+?\]>\s*)(.+?)(\s* { assigns[:content] } : nil assigns_binding = context.get_binding(&proc) # Get result safe_level = params[:safe_level] trim_mode = params[:trim_mode] erb = ::ERB.new(content, safe_level, trim_mode) erb.filename = filename erb.result(assigns_binding) end end end nanoc-4.1.4/lib/nanoc/filters/rainpress.rb0000644000004100000410000000070712665031555020531 0ustar www-datawww-datamodule Nanoc::Filters # @api private class Rainpress < Nanoc::Filter requires 'rainpress' # Runs the content through [Rainpress](http://code.google.com/p/rainpress/). # Parameters passed to this filter will be passed on to Rainpress. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, params = {}) ::Rainpress.compress(content, params) end end end nanoc-4.1.4/lib/nanoc/filters/sass/0000755000004100000410000000000012665031555017143 5ustar www-datawww-datananoc-4.1.4/lib/nanoc/filters/sass/sass_filesystem_importer.rb0000644000004100000410000000102012665031555024617 0ustar www-datawww-data# @api private class ::Sass::Importers::Filesystem alias _orig_find _find def _find(dir, name, options) # Find filename full_filename, _syntax = ::Sass::Util.destructure(find_real_file(dir, name, options)) return nil if full_filename.nil? # Create dependency filter = options[:nanoc_current_filter] if filter item = filter.imported_filename_to_item(full_filename) filter.depend_on([item]) unless item.nil? end # Call original _find _orig_find(dir, name, options) end end nanoc-4.1.4/lib/nanoc/filters/rdoc.rb0000644000004100000410000000111412665031555017443 0ustar www-datawww-datamodule Nanoc::Filters # @api private class RDoc < Nanoc::Filter requires 'rdoc' def self.setup gem 'rdoc', '~> 4.0' super end # Runs the content through [RDoc::Markup](http://docs.seattlerb.org/rdoc/RDoc/Markup.html). # This method takes no options. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, _params = {}) options = ::RDoc::Options.new to_html = ::RDoc::Markup::ToHtml.new(options) ::RDoc::Markup.new.convert(content, to_html) end end end nanoc-4.1.4/lib/nanoc/filters/asciidoc.rb0000644000004100000410000000105612665031555020277 0ustar www-datawww-datamodule Nanoc::Filters # @since 3.2.0 # # @api private class AsciiDoc < Nanoc::Filter # Runs the content through [AsciiDoc](http://www.methods.co.nz/asciidoc/). # This method takes no options. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, _params = {}) stdout = StringIO.new stderr = $stderr piper = Nanoc::Extra::Piper.new(stdout: stdout, stderr: stderr) piper.run(%w( asciidoc -o - - ), content) stdout.string end end end nanoc-4.1.4/lib/nanoc/filters/haml.rb0000644000004100000410000000125012665031555017436 0ustar www-datawww-datamodule Nanoc::Filters # @api private class Haml < Nanoc::Filter requires 'haml' # Runs the content through [Haml](http://haml-lang.com/). # Parameters passed to this filter will be passed on to Haml. # # @param [String] content The content to filter # # @return [String] The filtered content def run(content, params = {}) # Get options options = params.merge(filename: filename) # Create context context = ::Nanoc::Int::Context.new(assigns) # Get result proc = assigns[:content] ? -> { assigns[:content] } : nil ::Haml::Engine.new(content, options).render(context, assigns, &proc) end end end nanoc-4.1.4/lib/nanoc/filters/colorize_syntax.rb0000644000004100000410000003107312665031555021757 0ustar www-datawww-datamodule Nanoc::Filters # @api private class ColorizeSyntax < Nanoc::Filter requires 'nokogiri', 'stringio', 'open3' # The default colorizer to use for a language if the colorizer for that # language is not overridden. DEFAULT_COLORIZER = :coderay # Syntax-highlights code blocks in the given content. Code blocks should # be enclosed in `pre` elements that contain a `code` element. The code # element should have an indication of the language the code is in. There # are two possible ways of adding such an indication: # # 1. A HTML class starting with `language-` and followed by the # code language, as specified by HTML5. For example, ``. # # 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) %() 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 = "![Alt](/path/to/img 'Title')" output_expected = %r{Alt} 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 = "![Alt](/path/to/img 'Title')" output_expected = %r{Alt} 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

A 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(//, actual_content) assert_match(/baz<\/bar>/ raw_content = <<-XML baz XML actual_content = filter.setup_and_run(raw_content, type: :xml, select: ['*/@boo']) assert_match(expected, actual_content) end end def test_filter_fragment_xml if_have 'nokogiri' do # 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 = <<-XML baz XML actual_content = filter.setup_and_run(raw_content, type: :xml, select: ['far/@href']) assert_match(//, actual_content) assert_match(/baz<\/far><\/bar>/, actual_content) end end def test_filter_xml_with_namespaces if_have 'nokogiri' do # 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 = <<-XML baz XML options = { type: :xml, namespaces: { ex: 'http://example.org' }, select: ['ex:a/@href'], } actual_content = filter.setup_and_run(raw_content, options) assert_match(//, actual_content) assert_match(/baz<\/a><\/bar>/, actual_content) end end def test_filter_xhtml if_have 'nokogiri' do # 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 = <<-XML bar XML actual_content = filter.setup_and_run(raw_content, type: :xhtml) assert_match(/]*href="..\/..\/..\/css"[^>]*\/>/, actual_content) assert_match(/', '
', '', '', '', ]) check = Nanoc::Extra::Checking::Checks::MixedContent.create(site) check.run assert_empty check.issues end end def test_root_relative_content with_site do |site| create_output_file('foo.html', [ '', '', '', '
', '', '', '', ]) check = Nanoc::Extra::Checking::Checks::MixedContent.create(site) check.run assert_empty check.issues end end def test_protocol_relative_content with_site do |site| create_output_file('foo.html', [ '', '', '', '
', '', '', '', ]) check = Nanoc::Extra::Checking::Checks::MixedContent.create(site) check.run assert_empty check.issues end end def test_document_relative_content with_site do |site| create_output_file('foo.html', [ '', '', '', '
', '', '', '', ]) check = Nanoc::Extra::Checking::Checks::MixedContent.create(site) check.run assert_empty check.issues end end def test_query_relative_content with_site do |site| create_output_file('foo.html', [ '', '', '', '
', '', '', '', ]) check = Nanoc::Extra::Checking::Checks::MixedContent.create(site) check.run assert_empty check.issues end end def test_fragment_relative_content with_site do |site| create_output_file('foo.html', [ '', '', '', '
', '', '', '', ]) check = Nanoc::Extra::Checking::Checks::MixedContent.create(site) check.run assert_empty check.issues end end def test_http_content with_site do |site| create_output_file('foo.html', [ '', '', '', '', '
', '', '', '', ]) check = Nanoc::Extra::Checking::Checks::MixedContent.create(site) check.run issues = check.issues.to_a assert_equal 8, issues.count descriptions = issues.map(&:description) issues.each do |issue| assert_equal 'output/foo.html', issue.subject end # The order of the reported issues is not important, so use this class's # `assert_include` helper to avoid asserting those details assert_include descriptions, 'mixed content include: http://nanoc.ws/logo.png' assert_include descriptions, 'mixed content include: HTTP://nanoc.ws/logo.png' assert_include descriptions, 'mixed content include: http://nanoc.ws/style.css' assert_include descriptions, 'mixed content include: http://nanoc.ws/app.js' assert_include descriptions, 'mixed content include: http://nanoc.ws/process.cgi' assert_include descriptions, 'mixed content include: http://nanoc.ws/preview.html' assert_include descriptions, 'mixed content include: http://nanoc.ws/theme-song.flac' assert_include descriptions, 'mixed content include: http://nanoc.ws/screencast.mkv' end end def test_inert_content with_site do |site| create_output_file('foo.html', [ 'The homepage', 'Content', '', '', '', '', '
', '', '', '', '

http://nanoc.ws/harmless-text

', ]) check = Nanoc::Extra::Checking::Checks::MixedContent.create(site) check.run assert_empty check.issues end end end nanoc-4.1.4/test/extra/checking/checks/test_internal_links.rb0000644000004100000410000000514212665031555024375 0ustar www-datawww-dataclass Nanoc::Extra::Checking::Checks::InternalLinksTest < Nanoc::TestCase def test_run with_site do |site| # Create files FileUtils.mkdir_p('output') FileUtils.mkdir_p('output/stuff') File.open('output/foo.txt', 'w') { |io| io.write('broken') } File.open('output/bar.html', 'w') { |io| io.write('not broken') } # Create check check = Nanoc::Extra::Checking::Checks::InternalLinks.create(site) check.run # Test assert check.issues.empty? end end def test_valid? with_site do |site| # Create files FileUtils.mkdir_p('output') FileUtils.mkdir_p('output/stuff') File.open('output/origin', 'w') { |io| io.write('hi') } File.open('output/foo', 'w') { |io| io.write('hi') } File.open('output/stuff/blah', 'w') { |io| io.write('hi') } # Create check check = Nanoc::Extra::Checking::Checks::InternalLinks.create(site) # Test assert check.send(:valid?, 'foo', 'output/origin') assert check.send(:valid?, 'origin', 'output/origin') assert check.send(:valid?, 'stuff/blah', 'output/origin') assert check.send(:valid?, '/foo', 'output/origin') assert check.send(:valid?, '/origin', 'output/origin') assert check.send(:valid?, '/stuff/blah', 'output/origin') end end def test_remove_query_string with_site do |site| FileUtils.mkdir_p('output/stuff') File.open('output/stuff/right', 'w') { |io| io.write('hi') } check = Nanoc::Extra::Checking::Checks::InternalLinks.create(site) assert check.send(:valid?, 'stuff/right?foo=123', 'output/origin') refute check.send(:valid?, 'stuff/wrong?foo=123', 'output/origin') end end def test_exclude with_site do |site| # Create check check = Nanoc::Extra::Checking::Checks::InternalLinks.create(site) site.config.update({ checks: { internal_links: { exclude: ['^/excluded\d+'] } } }) # Test assert check.send(:valid?, '/excluded1', 'output/origin') assert check.send(:valid?, '/excluded2', 'output/origin') assert !check.send(:valid?, '/excluded_not', 'output/origin') end end def test_unescape_url with_site do |site| FileUtils.mkdir_p('output/stuff') File.open('output/stuff/right foo', 'w') { |io| io.write('hi') } check = Nanoc::Extra::Checking::Checks::InternalLinks.create(site) assert check.send(:valid?, 'stuff/right%20foo', 'output/origin') refute check.send(:valid?, 'stuff/wrong%20foo', 'output/origin') end end end nanoc-4.1.4/test/extra/checking/test_runner.rb0000644000004100000410000000211412665031555021426 0ustar www-datawww-dataclass Nanoc::Extra::Checking::RunnerTest < Nanoc::TestCase def test_run_specific with_site do |site| File.open('output/blah', 'w') { |io| io.write('I am stale! Haha!') } runner = Nanoc::Extra::Checking::Runner.new(site) runner.run_specific(%w( stale )) end end def test_run_specific_custom with_site do |site| File.open('Checks', 'w') do |io| io.write('check :my_foo_check do ; puts "I AM FOO!" ; end') end runner = Nanoc::Extra::Checking::Runner.new(site) ios = capturing_stdio do runner.run_specific(%w( my_foo_check )) end assert ios[:stdout].include?('I AM FOO!') end end def test_list_checks with_site do |site| File.open('Checks', 'w') do |io| io.write('check :my_foo_check do ; end') end runner = Nanoc::Extra::Checking::Runner.new(site) ios = capturing_stdio do runner.list_checks end assert ios[:stdout].include?('my_foo_check') assert ios[:stdout].include?('internal_links') assert ios[:stderr].empty? end end end nanoc-4.1.4/test/extra/checking/test_dsl.rb0000644000004100000410000000127412665031555020705 0ustar www-datawww-dataclass Nanoc::Extra::Checking::DSLTest < Nanoc::TestCase def test_from_file with_site do |_site| File.open('Checks', 'w') { |io| io.write("check :foo do\n\nend\ndeploy_check :bar\n") } dsl = Nanoc::Extra::Checking::DSL.from_file('Checks') # One new check refute Nanoc::Extra::Checking::Check.named(:foo).nil? # One check marked for deployment assert_equal [:bar], dsl.deploy_checks end end def test_has_base_path with_site do |_site| File.write('stuff.rb', '$greeting = "hello"') File.write('Checks', 'require "./stuff"') Nanoc::Extra::Checking::DSL.from_file('Checks') assert_equal 'hello', $greeting end end end nanoc-4.1.4/test/extra/test_link_collector.rb0000644000004100000410000000602712665031555021354 0ustar www-datawww-dataclass Nanoc::Extra::LinkCollectorTest < Nanoc::TestCase def test_all # Create dummy data File.open('file-a.html', 'w') do |io| io << %(A 1 ) io << %(A 2 ) io << %( ) io << %(A 4) io << %(A 5 ) end File.open('file-b.html', 'w') do |io| io << %(B 1 ) io << %(B 2 ) io << %(B 3 ) end # Create validator collector = Nanoc::Extra::LinkCollector.new(%w( file-a.html file-b.html )) # Test hrefs_with_filenames = collector.filenames_per_href hrefs = hrefs_with_filenames.keys assert_includes hrefs, 'http://example.com/' assert_includes hrefs, 'https://example.com/' assert_includes hrefs, 'stuff/' refute_includes hrefs, nil assert_includes hrefs, 'mailto:bob@example.com' assert_includes hrefs, '../stuff' assert_includes hrefs, '/stuff' refute_includes hrefs, 'https://example.com/with-fragment#moo' assert_includes hrefs, 'https://example.com/with-fragment' end def test_external # Create dummy data File.open('file-a.html', 'w') do |io| io << %(A 1 ) io << %(A 2 ) io << %( ) end File.open('file-b.html', 'w') do |io| io << %(B 1 ) io << %(B 2 ) io << %(B 3 ) end # Create validator collector = Nanoc::Extra::LinkCollector.new(%w( file-a.html file-b.html ), :external) # Test hrefs_with_filenames = collector.filenames_per_href hrefs = hrefs_with_filenames.keys assert_includes hrefs, 'http://example.com/' assert_includes hrefs, 'https://example.com/' refute_includes hrefs, 'stuff/' assert_includes hrefs, 'mailto:bob@example.com' refute_includes hrefs, '../stuff' refute_includes hrefs, '/stuff' end def test_internal # Create dummy data File.open('file-a.html', 'w') do |io| io << %(A 1 ) io << %(A 2 ) io << %( ) end File.open('file-b.html', 'w') do |io| io << %(B 1 ) io << %(B 2 ) io << %(B 3 ) end # Create validator collector = Nanoc::Extra::LinkCollector.new(%w( file-a.html file-b.html ), :internal) # Test hrefs_with_filenames = collector.filenames_per_href hrefs = hrefs_with_filenames.keys refute_includes hrefs, 'http://example.com/' refute_includes hrefs, 'https://example.com/' assert_includes hrefs, 'stuff/' refute_includes hrefs, 'mailto:bob@example.com' assert_includes hrefs, '../stuff' assert_includes hrefs, '/stuff' end end nanoc-4.1.4/test/rule_dsl/0000755000004100000410000000000012665031555015446 5ustar www-datawww-datananoc-4.1.4/test/rule_dsl/test_rules_collection.rb0000644000004100000410000000623712665031555022407 0ustar www-datawww-dataclass Nanoc::RuleDSL::RulesCollectionTest < Nanoc::TestCase def test_compilation_rule_for # Mock rules rules = [mock, mock, mock] rules[0].expects(:applicable_to?).returns(false) rules[1].expects(:applicable_to?).returns(true) rules[1].expects(:rep_name).returns('wrong') rules[2].expects(:applicable_to?).returns(true) rules[2].expects(:rep_name).returns('right') rules_collection = Nanoc::RuleDSL::RulesCollection.new rules_collection.instance_eval { @item_compilation_rules = rules } # Mock rep rep = mock rep.stubs(:name).returns('right') item = mock rep.stubs(:item).returns(item) # Test assert_equal rules[2], rules_collection.compilation_rule_for(rep) end def test_filter_for_layout_with_existant_layout rules_collection = Nanoc::RuleDSL::RulesCollection.new rules_collection.layout_filter_mapping[Nanoc::Int::Pattern.from(/.*/)] = [:erb, { foo: 'bar' }] # Mock layout layout = MiniTest::Mock.new layout.expect(:identifier, '/some_layout/') # Check assert_equal([:erb, { foo: 'bar' }], rules_collection.filter_for_layout(layout)) end def test_filter_for_layout_with_existant_layout_and_unknown_filter rules_collection = Nanoc::RuleDSL::RulesCollection.new rules_collection.layout_filter_mapping[Nanoc::Int::Pattern.from(/.*/)] = [:some_unknown_filter, { foo: 'bar' }] # Mock layout layout = MiniTest::Mock.new layout.expect(:identifier, '/some_layout/') # Check assert_equal([:some_unknown_filter, { foo: 'bar' }], rules_collection.filter_for_layout(layout)) end def test_filter_for_layout_with_nonexistant_layout rules_collection = Nanoc::RuleDSL::RulesCollection.new rules_collection.layout_filter_mapping[Nanoc::Int::Pattern.from(%r{^/foo/$})] = [:erb, { foo: 'bar' }] # Mock layout layout = MiniTest::Mock.new layout.expect(:identifier, '/bar/') # Check assert_equal(nil, rules_collection.filter_for_layout(layout)) end def test_filter_for_layout_with_many_layouts rules_collection = Nanoc::RuleDSL::RulesCollection.new rules_collection.layout_filter_mapping[Nanoc::Int::Pattern.from(%r{^/a/b/c/.*/$})] = [:erb, { char: 'd' }] rules_collection.layout_filter_mapping[Nanoc::Int::Pattern.from(%r{^/a/.*/$})] = [:erb, { char: 'b' }] rules_collection.layout_filter_mapping[Nanoc::Int::Pattern.from(%r{^/a/b/.*/$})] = [:erb, { char: 'c' }] # never used! rules_collection.layout_filter_mapping[Nanoc::Int::Pattern.from(%r{^/.*/$})] = [:erb, { char: 'a' }] # Mock layout layouts = [mock, mock, mock, mock] layouts[0].stubs(:identifier).returns('/a/b/c/d/') layouts[1].stubs(:identifier).returns('/a/b/c/') layouts[2].stubs(:identifier).returns('/a/b/') layouts[3].stubs(:identifier).returns('/a/') # Get expectations expectations = { 0 => 'd', 1 => 'b', # never used! not c, because b takes priority 2 => 'b', 3 => 'a', } # Check expectations.each_pair do |num, char| filter_and_args = rules_collection.filter_for_layout(layouts[num]) refute_nil(filter_and_args) assert_equal(char, filter_and_args[1][:char]) end end end nanoc-4.1.4/test/rule_dsl/test_compiler_dsl.rb0000644000004100000410000002656212665031555021521 0ustar www-datawww-dataclass Nanoc::RuleDSL::CompilerDSLTest < Nanoc::TestCase def test_compile # TODO: implement end def test_route # TODO: implement end def test_layout # TODO: implement end def test_preprocess_twice rules_collection = Nanoc::RuleDSL::RulesCollection.new compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(rules_collection, {}) # first time io = capturing_stdio do compiler_dsl.preprocess {} end assert_empty io[:stdout] assert_empty io[:stderr] # second time io = capturing_stdio do compiler_dsl.preprocess {} end assert_empty io[:stdout] assert_match(/WARNING: A preprocess block is already defined./, io[:stderr]) end def test_postprocess_twice rules_collection = Nanoc::RuleDSL::RulesCollection.new compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(rules_collection, {}) # first time io = capturing_stdio do compiler_dsl.postprocess {} end assert_empty io[:stdout] assert_empty io[:stderr] # second time io = capturing_stdio do compiler_dsl.postprocess {} end assert_empty io[:stdout] assert_match(/WARNING: A postprocess block is already defined./, io[:stderr]) end def test_postprocessor_modified_method with_site do |site| # Create rules File.open('Rules', 'w') do |io| io.write <" end # Compile site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile # Check paths assert_equal ['output/foo'], Dir['output/*'] assert_equal ['output/foo/index.html'], Dir['output/foo/*'] end end def test_passthrough_with_full_identifiers with_site do File.open('nanoc.yaml', 'w') do |io| io << 'string_pattern_type: legacy' << "\n" io << 'data_sources:' << "\n" io << ' -' << "\n" io << ' type: filesystem' << "\n" io << ' identifier_type: full' << "\n" end # Create rules File.open('Rules', 'w') do |io| io << 'passthrough \'*\'' end # Create items assert Dir['content/*'].empty? File.open('content/robots.txt', 'w') do |io| io.write 'Hello I am robots' end # Compile site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile # Check paths assert_equal ['output/robots.txt'], Dir['output/*'] end end def test_ignore with_site do # Create rules File.open('Rules', 'w') do |io| io.write <" end # Compile site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile # Check paths assert_equal ['output/foo'], Dir['output/*'] assert_equal ['output/foo/index.html'], Dir['output/foo/*'] end end def test_create_pattern_with_string_with_no_config compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) err = assert_raises(Nanoc::Int::Errors::GenericTrivial) do compiler_dsl.create_pattern('/foo/*') end assert_equal 'Invalid string_pattern_type: ', err.message end def test_create_pattern_with_string_with_glob_string_pattern_type compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, { string_pattern_type: 'glob' }) pattern = compiler_dsl.create_pattern('/foo/*') assert pattern.match?('/foo/aaaa') refute pattern.match?('/foo/aaaa/') refute pattern.match?('/foo/a/a/a/a') end def test_create_pattern_with_regex compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, { string_pattern_type: 'glob' }) pattern = compiler_dsl.create_pattern(%r{\A/foo/a*/}) assert pattern.match?('/foo/aaaa/') end def test_create_pattern_with_string_with_unknown_string_pattern_type compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, { string_pattern_type: 'donkey' }) err = assert_raises(Nanoc::Int::Errors::GenericTrivial) do compiler_dsl.create_pattern('/foo/*') end assert_equal 'Invalid string_pattern_type: donkey', err.message end def test_identifier_to_regex_without_wildcards # Create compiler DSL compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) actual = compiler_dsl.instance_eval { identifier_to_regex('foo') } expected = %r{^/foo/$} assert_equal(expected.to_s, actual.to_s) assert_equal(expected.source, actual.source) assert_equal(expected.kcode, actual.kcode) if expected.respond_to?(:kcode) assert_equal(expected.casefold?, actual.casefold?) assert_equal(expected.options, actual.options) end def test_identifier_to_regex_with_one_wildcard # Create compiler DSL compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) actual = compiler_dsl.instance_eval { identifier_to_regex('foo/*/bar') } expected = %r{^/foo/(.*?)/bar/$} assert_equal(expected.to_s, actual.to_s) assert_equal(expected.source, actual.source) assert_equal(expected.kcode, actual.kcode) if expected.respond_to?(:kcode) assert_equal(expected.casefold?, actual.casefold?) assert_equal(expected.options, actual.options) end def test_identifier_to_regex_with_two_wildcards # Create compiler DSL compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) actual = compiler_dsl.instance_eval { identifier_to_regex('foo/*/bar/*/qux') } expected = %r{^/foo/(.*?)/bar/(.*?)/qux/$} assert_equal(expected.to_s, actual.to_s) assert_equal(expected.source, actual.source) assert_equal(expected.kcode, actual.kcode) if expected.respond_to?(:kcode) assert_equal(expected.casefold?, actual.casefold?) assert_equal(expected.options, actual.options) end def test_identifier_to_regex_with_just_one_wildcard # Create compiler DSL compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) actual = compiler_dsl.instance_eval { identifier_to_regex('*') } expected = %r{^/(.*?)$} assert_equal(expected.to_s, actual.to_s) assert_equal(expected.source, actual.source) assert_equal(expected.kcode, actual.kcode) if expected.respond_to?(:kcode) assert_equal(expected.casefold?, actual.casefold?) assert_equal(expected.options, actual.options) end def test_identifier_to_regex_with_root # Create compiler DSL compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) actual = compiler_dsl.instance_eval { identifier_to_regex('/') } expected = %r{^/$} assert_equal(expected.to_s, actual.to_s) assert_equal(expected.source, actual.source) assert_equal(expected.kcode, actual.kcode) if expected.respond_to?(:kcode) assert_equal(expected.casefold?, actual.casefold?) assert_equal(expected.options, actual.options) end def test_identifier_to_regex_with_only_children # Create compiler DSL compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) actual = compiler_dsl.instance_eval { identifier_to_regex('/foo/*/') } expected = %r{^/foo/(.*?)/$} assert_equal(expected.to_s, actual.to_s) assert_equal(expected.source, actual.source) assert_equal(expected.kcode, actual.kcode) if expected.respond_to?(:kcode) assert_equal(expected.casefold?, actual.casefold?) assert_equal(expected.options, actual.options) end def test_identifier_to_regex_with_plus_wildcard # Create compiler DSL compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) actual = compiler_dsl.instance_eval { identifier_to_regex('/foo/+') } expected = %r{^/foo/(.+?)/$} assert_equal(expected.to_s, actual.to_s) assert_equal(expected.source, actual.source) assert_equal(expected.kcode, actual.kcode) if expected.respond_to?(:kcode) assert_equal(expected.casefold?, actual.casefold?) assert_equal(expected.options, actual.options) assert('/foo/bar/' =~ actual) refute('/foo/' =~ actual) end def test_dsl_has_no_access_to_compiler compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, {}) assert_raises(NameError) do compiler_dsl.instance_eval { compiler } end end def test_config $venetian = 'unsnares' compiler_dsl = Nanoc::RuleDSL::CompilerDSL.new(nil, { venetian: 'snares' }) compiler_dsl.instance_eval { $venetian = @config[:venetian] } assert_equal 'snares', $venetian end end nanoc-4.1.4/test/rule_dsl/test_action_provider.rb0000644000004100000410000000436612665031555022232 0ustar www-datawww-dataclass Nanoc::RuleDSL::ActionProviderTest < Nanoc::TestCase def new_action_provider(site) rules_collection = Nanoc::RuleDSL::RulesCollection.new rule_memory_calculator = Nanoc::RuleDSL::RuleMemoryCalculator.new( rules_collection: rules_collection, site: site) action_provider = Nanoc::RuleDSL::ActionProvider.new( rules_collection, rule_memory_calculator) Nanoc::RuleDSL::RulesLoader.new(site.config, rules_collection).load action_provider end def test_per_rules_file_preprocessor # Create site Nanoc::CLI.run %w( create_site foo ) FileUtils.cd('foo') do # Create a bonus rules file File.write( 'more_rules.rb', "preprocess { @items['/index.*'][:preprocessed] = true }") # Adjust normal rules file File.write( 'Rules', "include_rules 'more_rules'\n\npreprocess {}\n\n" + File.read('Rules')) # Create site and compiler site = Nanoc::Int::SiteLoader.new.new_from_cwd action_provider = new_action_provider(site) # Check that the two preprocess blocks have been added assert_equal 2, action_provider.rules_collection.preprocessors.size refute_nil action_provider.rules_collection.preprocessors.first refute_nil action_provider.rules_collection.preprocessors.to_a.last # Apply preprocess blocks action_provider.preprocess(site) assert site.items['/index.*'].attributes[:preprocessed] end end def test_per_rules_file_postprocessor # Create site Nanoc::CLI.run %w( create_site foo ) FileUtils.cd('foo') do # Create a bonus rules file File.write( 'more_rules.rb', 'postprocess {}') # Adjust normal rules file File.write( 'Rules', "include_rules 'more_rules'\n\npostprocess {}\n\n" + File.read('Rules')) # Create site and compiler site = Nanoc::Int::SiteLoader.new.new_from_cwd action_provider = new_action_provider(site) # Check that the two postprocess blocks have been added assert_equal 2, action_provider.rules_collection.postprocessors.size refute_nil action_provider.rules_collection.postprocessors.first refute_nil action_provider.rules_collection.postprocessors.to_a.last end end end nanoc-4.1.4/test/rule_dsl/test_rule.rb0000644000004100000410000000074112665031555020003 0ustar www-datawww-dataclass Nanoc::Int::RuleTest < Nanoc::TestCase def test_initialize # TODO: implement end def test_applicable_to # TODO: implement end def test_apply_to # TODO: implement end def test_matches pattern = Nanoc::Int::Pattern.from(%r{/(.*)/(.*)/}) identifier = '/anything/else/' expected = %w(anything else) rule = Nanoc::RuleDSL::Rule.new(pattern, :string, proc {}) assert_equal expected, rule.send(:matches, identifier) end end nanoc-4.1.4/test/cli/0000755000004100000410000000000012665031555014404 5ustar www-datawww-datananoc-4.1.4/test/cli/commands/0000755000004100000410000000000012665031555016205 5ustar www-datawww-datananoc-4.1.4/test/cli/commands/test_compile.rb0000644000004100000410000001506312665031555021226 0ustar www-datawww-dataclass Nanoc::CLI::Commands::CompileTest < Nanoc::TestCase def test_profiling_information with_site do |_site| File.open('content/foo.md', 'w') { |io| io << 'asdf' } File.open('content/bar.md', 'w') { |io| io << 'asdf' } File.open('content/baz.md', 'w') { |io| io << 'asdf' } 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 " if item.binary?\n" io.write " item.identifier.chop + '.' + item[:extension]\n" io.write " else\n" io.write " item.identifier + 'index.html'\n" io.write " end\n" io.write "end\n" io.write "\n" io.write "layout '*', :erb\n" end Nanoc::CLI.run %w( compile --verbose ) end end def test_auto_prune with_site do |_site| File.open('content/foo.md', 'w') { |io| io << 'asdf' } File.open('content/bar.md', 'w') { |io| io << 'asdf' } File.open('content/baz.md', 'w') { |io| io << 'asdf' } 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 " if item.binary?\n" io.write " item.identifier.chop + '.' + item[:extension]\n" io.write " else\n" io.write " item.identifier + 'index.html'\n" io.write " end\n" io.write "end\n" io.write "\n" io.write "layout '*', :erb\n" end File.open('nanoc.yaml', 'w') do |io| io.write "string_pattern_type: legacy\n" io.write "prune:\n" io.write " auto_prune: false\n" end File.open('output/stray.html', 'w') do |io| io.write 'I am a stray file and I am about to be deleted!' end assert File.file?('output/stray.html') Nanoc::CLI.run %w( compile ) assert File.file?('output/stray.html') File.open('nanoc.yaml', 'w') do |io| io.write "string_pattern_type: legacy\n" io.write "prune:\n" io.write " auto_prune: true\n" end assert File.file?('output/stray.html') Nanoc::CLI.run %w( compile ) refute File.file?('output/stray.html') end end def test_auto_prune_with_exclude with_site do |_site| File.open('content/foo.md', 'w') { |io| io << 'asdf' } File.open('content/bar.md', 'w') { |io| io << 'asdf' } File.open('content/baz.md', 'w') { |io| io << 'asdf' } 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 " if item.binary?\n" io.write " item.identifier.chop + '.' + item[:extension]\n" io.write " else\n" io.write " item.identifier + 'index.html'\n" io.write " end\n" io.write "end\n" io.write "\n" io.write "layout '*', :erb\n" end Dir.mkdir('output/excluded_dir') File.open('nanoc.yaml', 'w') do |io| io.write "string_pattern_type: legacy\n" io.write "prune:\n" io.write " auto_prune: false\n" end File.open('output/stray.html', 'w') do |io| io.write 'I am a stray file and I am about to be deleted!' end assert File.file?('output/stray.html') Nanoc::CLI.run %w( compile ) assert File.file?('output/stray.html') File.open('nanoc.yaml', 'w') do |io| io.write "string_pattern_type: legacy\n" io.write "prune:\n" io.write " auto_prune: true\n" io.write " exclude: [ 'excluded_dir' ]\n" end assert File.file?('output/stray.html') Nanoc::CLI.run %w( compile ) refute File.file?('output/stray.html') assert File.directory?('output/excluded_dir'), 'excluded_dir should still be there' end end def test_setup_and_teardown_listeners with_site do test_listener_class = Class.new(::Nanoc::CLI::Commands::Compile::Listener) do def start @started = true end def stop @stopped = true end def started? @started end def stopped? @stopped end end options = {} arguments = [] cmd = nil cmd_runner = Nanoc::CLI::Commands::Compile.new(options, arguments, cmd) cmd_runner.listener_classes = [test_listener_class] cmd_runner.run listeners = cmd_runner.send(:listeners) assert listeners.size == 1 assert listeners.first.started? assert listeners.first.stopped? end end def test_file_action_printer_normal # Create data item = Nanoc::Int::Item.new('content', {}, '/') rep = Nanoc::Int::ItemRep.new(item, :default) rep.raw_paths[:last] = 'output/foo.txt' rep.compiled = true # Listen listener = new_file_action_printer([rep]) listener.start Nanoc::Int::NotificationCenter.post(:compilation_started, rep) Nanoc::Int::NotificationCenter.post(:rep_written, rep, rep.raw_path, false, true) listener.stop # Check assert_equal 1, listener.events.size assert_equal :high, listener.events[0][:level] assert_equal :update, listener.events[0][:action] assert_equal 'output/foo.txt', listener.events[0][:path] assert_in_delta 0.0, listener.events[0][:duration], 1.0 end def test_file_action_printer_skip # Create data item = Nanoc::Int::Item.new('content', {}, '/') rep = Nanoc::Int::ItemRep.new(item, :default) rep.raw_paths[:last] = 'output/foo.txt' # Listen listener = new_file_action_printer([rep]) listener.start Nanoc::Int::NotificationCenter.post(:compilation_started, rep) listener.stop # Check assert_equal 1, listener.events.size assert_equal :low, listener.events[0][:level] assert_equal :skip, listener.events[0][:action] assert_equal 'output/foo.txt', listener.events[0][:path] assert_nil listener.events[0][:duration] end def new_file_action_printer(reps) # Ensure CLI is loaded begin Nanoc::CLI.run(%w( help %)) rescue SystemExit end listener = Nanoc::CLI::Commands::Compile::FileActionPrinter.new(reps: reps) def listener.log(level, action, path, duration) @events ||= [] @events << { level: level, action: action, path: path, duration: duration, } end def listener.events @events end listener end end nanoc-4.1.4/test/cli/commands/test_check.rb0000644000004100000410000000065112665031555020650 0ustar www-datawww-dataclass Nanoc::CLI::Commands::CheckTest < Nanoc::TestCase def test_check_stale with_site do |_site| FileUtils.mkdir_p('output') # Should not raise now Nanoc::CLI.run %w( check stale ) # Should raise now File.open('output/blah.html', 'w') { |io| io.write 'moo' } assert_raises Nanoc::Int::Errors::GenericTrivial do Nanoc::CLI.run %w( check stale ) end end end end nanoc-4.1.4/test/cli/commands/test_prune.rb0000644000004100000410000001174112665031555020726 0ustar www-datawww-dataclass Nanoc::CLI::Commands::PruneTest < Nanoc::TestCase def test_run_without_yes with_site do |_site| # Set output dir File.open('nanoc.yaml', 'w') { |io| io.write "output_dir: output2\nstring_pattern_type: legacy\n" } FileUtils.mkdir_p('output2') # Create source files File.open('content/index.html', 'w') { |io| io.write 'stuff' } # Create output files File.open('output2/foo.html', 'w') { |io| io.write 'this is a foo.' } File.open('output2/index.html', 'w') { |io| io.write 'this is a index.' } assert_raises SystemExit do Nanoc::CLI.run %w( prune ) end assert File.file?('output2/index.html') assert File.file?('output2/foo.html') end end def test_run_with_yes with_site do |_site| # Set output dir File.open('nanoc.yaml', 'w') do |io| io << 'output_dir: output2' << "\n" io << 'string_pattern_type: legacy' << "\n" io << 'data_sources:' << "\n" io << ' -' << "\n" io << ' type: filesystem' << "\n" io << ' identifier_type: legacy' << "\n" end FileUtils.mkdir_p('output2') # Create source files File.open('content/index.html', 'w') { |io| io.write 'stuff' } # Create output files File.open('output2/foo.html', 'w') { |io| io.write 'this is a foo.' } File.open('output2/index.html', 'w') { |io| io.write 'this is a index.' } Nanoc::CLI.run %w( prune --yes ) assert File.file?('output2/index.html') assert !File.file?('output2/foo.html') end end def test_run_with_dry_run with_site do |_site| # Set output dir File.open('nanoc.yaml', 'w') { |io| io.write "string_pattern_type: legacy\noutput_dir: output2" } FileUtils.mkdir_p('output2') # Create source files File.open('content/index.html', 'w') { |io| io.write 'stuff' } # Create output files File.open('output2/foo.html', 'w') { |io| io.write 'this is a foo.' } File.open('output2/index.html', 'w') { |io| io.write 'this is a index.' } Nanoc::CLI.run %w( prune --dry-run ) assert File.file?('output2/index.html') assert File.file?('output2/foo.html') end end def test_run_with_exclude with_site do |_site| # Set output dir File.open('nanoc.yaml', 'w') do |io| io << 'prune:' << "\n" io << ' exclude: [ "good-dir", "good-file.html" ]' << "\n" io << 'string_pattern_type: legacy' << "\n" io << 'data_sources:' << "\n" io << ' -' << "\n" io << ' type: filesystem' << "\n" io << ' identifier_type: legacy' << "\n" end FileUtils.mkdir_p('output') # Create source files File.open('content/index.html', 'w') { |io| io.write 'stuff' } # Create output files FileUtils.mkdir_p('output/good-dir') FileUtils.mkdir_p('output/bad-dir') File.open('output/good-file.html', 'w') { |io| io.write 'stuff' } File.open('output/good-dir/blah', 'w') { |io| io.write 'stuff' } File.open('output/bad-file.html', 'w') { |io| io.write 'stuff' } File.open('output/bad-dir/blah', 'w') { |io| io.write 'stuff' } File.open('output/index.html', 'w') { |io| io.write 'stuff' } Nanoc::CLI.run %w( prune --yes ) assert File.file?('output/index.html') assert File.file?('output/good-dir/blah') assert File.file?('output/good-file.html') assert !File.file?('output/bad-dir/blah') assert !File.file?('output/bad-file.html') end end def test_run_with_symlink_to_output_dir skip_unless_symlinks_supported if defined?(JRUBY_VERSION) skip 'JRuby has buggy File.find behavior (see https://github.com/jruby/jruby/issues/1647)' end with_site do |_site| # Set output dir FileUtils.rm_rf('output') FileUtils.mkdir_p('output-real') File.symlink('output-real', 'output') # Create source files File.open('content/index.html', 'w') { |io| io.write 'stuff' } # Create output files FileUtils.mkdir_p('output-real/some-dir') File.open('output-real/some-file.html', 'w') { |io| io.write 'stuff' } File.open('output-real/index.html', 'w') { |io| io.write 'stuff' } Nanoc::CLI.run %w( prune --yes ) assert File.file?('output-real/index.html') assert !File.directory?('output-real/some-dir') assert !File.file?('output-real/some-file.html') end end def test_run_with_nested_empty_dirs with_site do |_site| # Set output dir File.open('nanoc.yaml', 'w') { |io| io.write 'output_dir: output' } FileUtils.mkdir_p('output') # Create output files FileUtils.mkdir_p('output/a/b/c') File.open('output/a/b/c/index.html', 'w') { |io| io.write 'stuff' } Nanoc::CLI.run %w( prune --yes ) assert !File.file?('output/a/b/c/index.html') assert !File.directory?('output/a/b/c') assert !File.directory?('output/a/b') assert !File.directory?('output/a') end end end nanoc-4.1.4/test/cli/commands/test_info.rb0000644000004100000410000000015612665031555020526 0ustar www-datawww-dataclass Nanoc::CLI::Commands::InfoTest < Nanoc::TestCase def test_run Nanoc::CLI.run %w( info ) end end nanoc-4.1.4/test/cli/commands/test_create_site.rb0000644000004100000410000000636512665031555022072 0ustar www-datawww-dataclass Nanoc::CLI::Commands::CreateSiteTest < Nanoc::TestCase def test_create_site_with_existing_name Nanoc::CLI.run %w( create_site foo ) assert_raises(::Nanoc::Int::Errors::GenericTrivial) do Nanoc::CLI.run %w( create_site foo ) end end def test_can_compile_new_site Nanoc::CLI.run %w( create_site foo ) FileUtils.cd('foo') do site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile end end def test_can_compile_new_site_in_current_directory FileUtils.mkdir('foo') FileUtils.cd('foo') do Nanoc::CLI.run %w( create_site ./ ) site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile end end def test_can_compile_new_site_with_binary_items Nanoc::CLI.run %w( create_site foo ) FileUtils.cd('foo') do File.open('content/blah', 'w') { |io| io << 'asdf' } site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile assert File.file?('output/blah') end end def test_can_compile_site_in_nonempty_directory FileUtils.mkdir('foo') FileUtils.touch(File.join('foo', 'SomeFile.txt')) Nanoc::CLI.run %w( create_site foo --force ) FileUtils.cd('foo') do site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile end end def test_compiled_site_output FileUtils.mkdir('foo') FileUtils.touch(File.join('foo', 'SomeFile.txt')) Nanoc::CLI.run %w( create_site foo --force ) FileUtils.cd('foo') do site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile assert File.file?('output/index.html') end end def test_default_encoding unless defined?(Encoding) skip 'No Encoding class' return end original_encoding = Encoding.default_external Encoding.default_external = 'ISO-8859-1' # ew! Nanoc::CLI.run %w( create_site foo ) FileUtils.cd('foo') do # Try with encoding = default encoding = utf-8 File.open('content/index.html', 'w') { |io| io.write("Hello <\xD6>!\n") } exception = assert_raises(RuntimeError) do Nanoc::Int::SiteLoader.new.new_from_cwd end assert_equal 'Could not read content/index.html because the file is not valid UTF-8.', exception.message # Try with encoding = specific File.open('nanoc.yaml', 'w') do |io| io.write("string_pattern_type: glob\n") io.write("data_sources:\n") io.write(" -\n") io.write(" type: filesystem\n") io.write(" identifier_type: full\n") end site = Nanoc::Int::SiteLoader.new.new_from_cwd site.compile end FileUtils ensure Encoding.default_external = original_encoding end def test_new_site_has_correct_stylesheets Nanoc::CLI.run %w( create_site foo ) FileUtils.cd('foo') do Nanoc::CLI.run %w( compile ) assert File.file?('content/stylesheet.css') assert_match(/\/stylesheet.css/, File.read('output/index.html')) end end def test_new_site_prunes_by_default FileUtils.mkdir('foo') FileUtils.touch(File.join('foo', 'SomeFile.txt')) Nanoc::CLI.run %w( create_site foo --force ) FileUtils.cd('foo') do File.write('output/blah.txt', 'stuff') Nanoc::CLI.run %w( compile ) refute File.file?('output/blah.txt') end end end nanoc-4.1.4/test/cli/commands/test_help.rb0000644000004100000410000000021712665031555020521 0ustar www-datawww-dataclass Nanoc::CLI::Commands::HelpTest < Nanoc::TestCase def test_run Nanoc::CLI.run %w( help ) Nanoc::CLI.run %w( help co ) end end nanoc-4.1.4/test/cli/commands/test_deploy.rb0000644000004100000410000001232612665031555021071 0ustar www-datawww-dataclass Nanoc::CLI::Commands::DeployTest < Nanoc::TestCase def test_deploy skip_unless_have_command 'rsync' with_site do |_site| File.open('nanoc.yaml', 'w') do |io| io.write "deploy:\n" io.write " public:\n" io.write " kind: rsync\n" io.write ' dst: mydestination' end FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } Nanoc::CLI.run %w( deploy -t public ) assert File.directory?('mydestination') assert File.file?('mydestination/blah.html') end end def test_deploy_with_dry_run with_site do |_site| File.open('nanoc.yaml', 'w') do |io| io.write "deploy:\n" io.write " public:\n" io.write " kind: rsync\n" io.write ' dst: mydestination' end FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } Nanoc::CLI.run %w( deploy -t public -n ) refute File.directory?('mydestination') refute File.file?('mydestination/blah.html') end end def test_deploy_with_list_without_config with_site do |_site| FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } ios = capturing_stdio do Nanoc::CLI.run %w( deploy -L ) end assert ios[:stdout].include?('No deployment configurations.') refute File.directory?('mydestination') refute File.file?('mydestination/blah.html') end end def test_deploy_with_list with_site do |_site| File.open('nanoc.yaml', 'w') do |io| io.write "deploy:\n" io.write " public:\n" io.write " kind: rsync\n" io.write ' dst: mydestination' end FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } ios = capturing_stdio do Nanoc::CLI.run %w( deploy -L ) end assert ios[:stdout].include?('Available deployment configurations:') refute File.directory?('mydestination') refute File.file?('mydestination/blah.html') end end def test_deploy_with_list_deployers with_site do |_site| File.open('nanoc.yaml', 'w') do |io| io.write "deploy:\n" io.write " public:\n" io.write " kind: rsync\n" io.write ' dst: mydestination' end FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } ios = capturing_stdio do Nanoc::CLI.run %w( deploy -D ) end assert ios[:stdout].include?('Available deployers:') refute File.directory?('mydestination') refute File.file?('mydestination/blah.html') end end def test_deploy_without_kind skip_unless_have_command 'rsync' with_site do |_site| File.open('nanoc.yaml', 'w') do |io| io.write "deploy:\n" io.write " public:\n" io.write ' dst: mydestination' end FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } ios = capturing_stdio do Nanoc::CLI.run %w( deploy -t public ) end assert ios[:stderr].include?('Warning: The specified deploy target does not have a kind attribute. Assuming rsync.') assert File.directory?('mydestination') assert File.file?('mydestination/blah.html') end end def test_deploy_without_target_without_default with_site do |_site| File.open('nanoc.yaml', 'w') do |io| io.write "deploy:\n" io.write " public:\n" io.write ' dst: mydestination' end FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } capturing_stdio do err = assert_raises Nanoc::Int::Errors::GenericTrivial do Nanoc::CLI.run %w( deploy ) end assert_equal 'The site has no deployment configuration for default.', err.message end end end def test_deploy_without_target_with_default skip_unless_have_command 'rsync' with_site do |_site| File.open('nanoc.yaml', 'w') do |io| io.write "deploy:\n" io.write " default:\n" io.write ' dst: mydestination' end FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } capturing_stdio do Nanoc::CLI.run %w( deploy ) end assert File.directory?('mydestination') assert File.file?('mydestination/blah.html') end end def test_deploy_with_preprocessor skip_unless_have_command 'rsync' with_site do |_site| File.open('nanoc.yaml', 'w') do |io| io.write "deploy:\n" io.write " default:\n" io.write ' dst: mydestination' end FileUtils.mkdir_p('output') File.open('output/blah.html', 'w') { |io| io.write 'moo' } File.write('Rules', "preprocess do ; @config[:deploy][:default][:dst] = 'otherdestination' ; end\n\n" + File.read('Rules')) capturing_stdio do Nanoc::CLI.run %w( deploy ) end refute File.directory?('mydestination') refute File.file?('mydestination/blah.html') assert File.directory?('otherdestination') assert File.file?('otherdestination/blah.html') end end end nanoc-4.1.4/test/cli/test_error_handler.rb0000644000004100000410000000353012665031555020617 0ustar www-datawww-dataclass Nanoc::CLI::ErrorHandlerTest < Nanoc::TestCase def setup super @handler = Nanoc::CLI::ErrorHandler.new end def test_resolution_for_with_unknown_gem error = LoadError.new('no such file to load -- afjlrestjlsgrshter') assert_nil @handler.send(:resolution_for, error) end def test_resolution_for_with_known_gem_without_bundler def @handler.using_bundler? false end error = LoadError.new('no such file to load -- kramdown') assert_match(/^Install the 'kramdown' gem using `gem install kramdown`./, @handler.send(:resolution_for, error)) end def test_resolution_for_with_known_gem_with_bundler def @handler.using_bundler? true end error = LoadError.new('no such file to load -- kramdown') assert_match(/^Make sure the gem is added to Gemfile/, @handler.send(:resolution_for, error)) end def test_resolution_for_with_not_load_error error = RuntimeError.new('nuclear meltdown detected') assert_nil @handler.send(:resolution_for, error) end def test_write_stack_trace_verbose error = new_error(20) stream = StringIO.new @handler.send(:write_stack_trace, stream, error, verbose: false) assert_match(/See full crash log for details./, stream.string) stream = StringIO.new @handler.send(:write_stack_trace, stream, error, verbose: false) assert_match(/See full crash log for details./, stream.string) stream = StringIO.new @handler.send(:write_stack_trace, stream, error, verbose: true) refute_match(/See full crash log for details./, stream.string) end def new_error(amount_factor) backtrace_generator = lambda do |af| if af == 0 raise 'finally!' else backtrace_generator.call(af - 1) end end begin backtrace_generator.call(amount_factor) rescue => e return e end end end nanoc-4.1.4/test/cli/test_cleaning_stream.rb0000644000004100000410000000273512665031555021132 0ustar www-datawww-dataclass Nanoc::CLI::CleaningStreamTest < Nanoc::TestCase class Stream attr_accessor :called_methods def initialize @called_methods = Set.new end def method_missing(symbol, *_args) @called_methods << symbol end end def test_forward methods = [:write, :<<, :tty?, :flush, :tell, :print, :puts, :string, :reopen, :exist?, :exists?, :close] s = Stream.new cs = Nanoc::CLI::CleaningStream.new(s) cs.write('aaa') cs << 'bb' cs.tty? cs.flush cs.tell cs.print('cc') cs.puts('dd') cs.string cs.reopen('/dev/null', 'r') cs.exist? cs.exists? cs.close methods.each do |m| assert s.called_methods.include?(m), "expected #{m} to be called" end end def test_works_with_logger require 'logger' stream = StringIO.new cleaning_stream = Nanoc::CLI::CleaningStream.new(stream) logger = Logger.new(cleaning_stream) logger.info('Some info') logger.warn('Something could start going wrong!') end def test_broken_pipe stream = StringIO.new def stream.write(_s) raise Errno::EPIPE.new end cleaning_stream = Nanoc::CLI::CleaningStream.new(stream) cleaning_stream.write('lol') end def test_non_string obj = Object.new def obj.to_s 'Hello… world!' end stream = StringIO.new cleaning_stream = Nanoc::CLI::CleaningStream.new(stream) cleaning_stream << obj assert_equal 'Hello… world!', stream.string end end nanoc-4.1.4/test/cli/test_logger.rb0000644000004100000410000000011112665031555017240 0ustar www-datawww-dataclass Nanoc::CLI::LoggerTest < Nanoc::TestCase def test_stub end end nanoc-4.1.4/test/cli/test_cli.rb0000644000004100000410000001245412665031555016545 0ustar www-datawww-dataclass Nanoc::CLITest < Nanoc::TestCase COMMAND_CODE = < 'en_US.ISO-8859-1', 'LC_CTYPE' => 'en_US.ISO-8859-1', 'LANG' => 'en_US.ISO-8859-1', } with_env_vars(new_env_diff) do io = StringIO.new def io.tty? true end refute Nanoc::CLI.enable_utf8?(io) io = StringIO.new def io.tty? false end assert Nanoc::CLI.enable_utf8?(io) end end def test_enable_utf8 io = StringIO.new def io.tty? true end new_env_diff = { 'LC_ALL' => 'en_US.ISO-8859-1', 'LC_CTYPE' => 'en_US.ISO-8859-1', 'LANG' => 'en_US.ISO-8859-1', } with_env_vars(new_env_diff) do refute Nanoc::CLI.enable_utf8?(io) with_env_vars({ 'LC_ALL' => 'en_US.UTF-8' }) { assert Nanoc::CLI.enable_utf8?(io) } with_env_vars({ 'LC_CTYPE' => 'en_US.UTF-8' }) { assert Nanoc::CLI.enable_utf8?(io) } with_env_vars({ 'LANG' => 'en_US.UTF-8' }) { assert Nanoc::CLI.enable_utf8?(io) } with_env_vars({ 'LC_ALL' => 'en_US.utf-8' }) { assert Nanoc::CLI.enable_utf8?(io) } with_env_vars({ 'LC_CTYPE' => 'en_US.utf-8' }) { assert Nanoc::CLI.enable_utf8?(io) } with_env_vars({ 'LANG' => 'en_US.utf-8' }) { assert Nanoc::CLI.enable_utf8?(io) } with_env_vars({ 'LC_ALL' => 'en_US.utf8' }) { assert Nanoc::CLI.enable_utf8?(io) } with_env_vars({ 'LC_CTYPE' => 'en_US.utf8' }) { assert Nanoc::CLI.enable_utf8?(io) } with_env_vars({ 'LANG' => 'en_US.utf8' }) { assert Nanoc::CLI.enable_utf8?(io) } end end end nanoc-4.1.4/test/helpers/0000755000004100000410000000000012665031555015277 5ustar www-datawww-datananoc-4.1.4/test/helpers/test_filtering.rb0000644000004100000410000000632612665031555020655 0ustar www-datawww-dataclass Nanoc::Helpers::FilteringTest < Nanoc::TestCase include Nanoc::Helpers::Filtering def test_filter_simple if_have 'rubypants' do # Build content to be evaluated content = "

Foo...

\n" \ "<% filter :rubypants do %>\n" \ "

Bar...

\n" \ "<% end %>\n" # Mock item and rep @item_rep = mock @item_rep = Nanoc::ItemRepView.new(@item_rep, nil) # Evaluate content result = ::ERB.new(content).result(binding) # Check assert_match('

Foo...

', result) assert_match('

Bar…

', result) end end def test_filter_with_assigns if_have 'rubypants' do content = "

Foo...

\n" \ "<% filter :erb do %>\n" \ "

<%%= @item[:title] %>

\n" \ "<% end %>\n" item = Nanoc::Int::Item.new('stuff', { title: 'Bar...' }, '/foo.md') item_rep = Nanoc::Int::ItemRep.new(item, :default) @item = Nanoc::ItemWithRepsView.new(item, nil) @item_rep = Nanoc::ItemRepView.new(item_rep, nil) result = ::ERB.new(content).result(binding) assert_match('

Foo...

', result) assert_match('

Bar...

', result) end end def test_filter_with_unknown_filter_name # Build content to be evaluated content = "

Foo...

\n" \ "<% filter :askjdflkawgjlkwaheflnvz do %>\n" \ "

Blah blah blah.

\n" \ "<% end %>\n" # Evaluate content assert_raises(Nanoc::Int::Errors::UnknownFilter) do ::ERB.new(content).result(binding) end end def test_filter_with_arguments if_have 'coderay' do # Build content to be evaluated content = "<% filter :erb, locals: { sheep: 'baah' } do %>\n" \ " Sheep says <%%= @sheep %>!\n" \ "<% end %>\n" # Mock item and rep @item_rep = mock @item_rep = Nanoc::ItemRepView.new(@item_rep, nil) # Evaluate content result = ::ERB.new(content).result(binding) assert_match(%r{Sheep says baah!}, result) end end def test_with_haml if_have 'haml' do # Build content to be evaluated content = "%p Foo.\n" \ "- filter(:erb) do\n" \ " <%= 'abc' + 'xyz' %>\n" \ "%p Bar.\n" # Mock item and rep @item_rep = mock @item_rep = Nanoc::ItemRepView.new(@item_rep, nil) # Evaluate content result = ::Haml::Engine.new(content).render(binding) assert_match(%r{^

Foo.

\s*abcxyz\s*

Bar.

$}, result) end end def test_notifications notifications = Set.new Nanoc::Int::NotificationCenter.on(:filtering_started) { notifications << :filtering_started } Nanoc::Int::NotificationCenter.on(:filtering_ended) { notifications << :filtering_ended } # Build content to be evaluated content = "<% filter :erb do %>\n" \ " ... stuff ...\n" \ "<% end %>\n" # Mock item and rep @item_rep = mock @item_rep = Nanoc::ItemRepView.new(@item_rep, nil) ::ERB.new(content).result(binding) assert notifications.include?(:filtering_started) assert notifications.include?(:filtering_ended) end end nanoc-4.1.4/test/helpers/test_tagging.rb0000644000004100000410000000514212665031555020305 0ustar www-datawww-dataclass Nanoc::Helpers::TaggingTest < Nanoc::TestCase include Nanoc::Helpers::Tagging def test_tags_for_without_tags # Create item item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('content', {}, '/path/'), nil) # Check assert_equal( '(none)', tags_for(item, base_url: 'http://example.com/tag/'), ) end def test_tags_for_with_custom_base_url # Create item item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('content', { tags: %w(foo bar) }, '/path/'), nil) # Check assert_equal( "#{link_for_tag('foo', 'http://stoneship.org/tag/')}, " \ "#{link_for_tag('bar', 'http://stoneship.org/tag/')}", tags_for(item, base_url: 'http://stoneship.org/tag/'), ) end def test_tags_for_with_custom_none_text # Create item item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('content', { tags: [] }, '/path/'), nil) # Check assert_equal( 'no tags for you, fool', tags_for(item, none_text: 'no tags for you, fool', base_url: 'http://example.com/tag/'), ) end def test_tags_for_with_custom_separator # Create item item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('content', { tags: %w(foo bar) }, '/path/'), nil) # Check assert_equal( "#{link_for_tag('foo', 'http://example.com/tag/')} ++ " \ "#{link_for_tag('bar', 'http://example.com/tag/')}", tags_for(item, separator: ' ++ ', base_url: 'http://example.com/tag/'), ) end def test_tags_for_without_base_url # Create item item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('content', { tags: %w(foo bar) }, '/path/'), nil) # Check assert_equal('foo, bar', tags_for(item)) end def test_items_with_tag # Create items @items = Nanoc::ItemCollectionWithRepsView.new( [ Nanoc::Int::Item.new('item 1', { tags: [:foo] }, '/item1/'), Nanoc::Int::Item.new('item 2', { tags: [:bar] }, '/item2/'), Nanoc::Int::Item.new('item 3', { tags: [:foo, :bar] }, '/item3/'), ], nil, ) # Find items items_with_foo_tag = items_with_tag(:foo) # Check assert_equal( [@items[0], @items[2]], items_with_foo_tag, ) end def test_link_for_tag assert_equal( %(), link_for_tag('foobar', 'http://stoneship.org/tags/'), ) end def test_link_for_tag_escape assert_equal( %(), link_for_tag('foo&bar', 'http://stoneship.org/tags&stuff/'), ) end end nanoc-4.1.4/test/helpers/test_breadcrumbs.rb0000644000004100000410000000444212665031555021160 0ustar www-datawww-dataclass Nanoc::Helpers::BreadcrumbsTest < Nanoc::TestCase include Nanoc::Helpers::Breadcrumbs def test_breadcrumbs_trail_at_root @items = Nanoc::Int::IdentifiableCollection.new({}) item = Nanoc::Int::Item.new('root', {}, Nanoc::Identifier.new('/', type: :legacy)) @items << item @item = item assert_equal [item], breadcrumbs_trail end def test_breadcrumbs_trail_with_1_parent @items = Nanoc::Int::IdentifiableCollection.new({}) parent_item = Nanoc::Int::Item.new('parent', {}, Nanoc::Identifier.new('/', type: :legacy)) child_item = Nanoc::Int::Item.new('child', {}, Nanoc::Identifier.new('/foo/', type: :legacy)) @items << parent_item @items << child_item @item = child_item assert_equal [parent_item, child_item], breadcrumbs_trail end def test_breadcrumbs_trail_with_many_parents @items = Nanoc::Int::IdentifiableCollection.new({}) grandparent_item = Nanoc::Int::Item.new('grandparent', {}, Nanoc::Identifier.new('/', type: :legacy)) parent_item = Nanoc::Int::Item.new('parent', {}, Nanoc::Identifier.new('/foo/', type: :legacy)) child_item = Nanoc::Int::Item.new('child', {}, Nanoc::Identifier.new('/foo/bar/', type: :legacy)) @items << grandparent_item @items << parent_item @items << child_item @item = child_item assert_equal [grandparent_item, parent_item, child_item], breadcrumbs_trail end def test_breadcrumbs_trail_with_nils @items = Nanoc::Int::IdentifiableCollection.new({}) grandparent_item = Nanoc::Int::Item.new('grandparent', {}, Nanoc::Identifier.new('/', type: :legacy)) child_item = Nanoc::Int::Item.new('child', {}, Nanoc::Identifier.new('/foo/bar/', type: :legacy)) @items << grandparent_item @items << child_item @item = child_item assert_equal [grandparent_item, nil, child_item], breadcrumbs_trail end def test_breadcrumbs_trail_with_non_legacy_identifiers @items = Nanoc::Int::IdentifiableCollection.new({}) parent_item = Nanoc::Int::Item.new('parent', {}, '/') child_item = Nanoc::Int::Item.new('child', {}, '/foo/') @items << parent_item @items << child_item @item = child_item assert_raises Nanoc::Helpers::Breadcrumbs::CannotGetBreadcrumbsForNonLegacyItem do breadcrumbs_trail end end end nanoc-4.1.4/test/helpers/test_text.rb0000644000004100000410000000134712665031555017654 0ustar www-datawww-dataclass Nanoc::Helpers::TextTest < Nanoc::TestCase include Nanoc::Helpers::Text def test_excerpt_length assert_equal('...', excerptize('Foo bar baz quux meow woof', length: 3)) assert_equal('Foo ...', excerptize('Foo bar baz quux meow woof', length: 7)) assert_equal('Foo bar baz quux meow woof', excerptize('Foo bar baz quux meow woof', length: 26)) assert_equal('Foo bar baz quux meow woof', excerptize('Foo bar baz quux meow woof', length: 8_623_785)) end def test_excerpt_omission assert_equal('Foo [continued]', excerptize('Foo bar baz quux meow woof', length: 15, omission: '[continued]')) end def test_strip_html # TODO: implement end end nanoc-4.1.4/test/helpers/test_xml_sitemap.rb0000644000004100000410000002045212665031555021210 0ustar www-datawww-dataclass Nanoc::Helpers::XMLSitemapTest < Nanoc::TestCase include Nanoc::Helpers::XMLSitemap def setup super @reps = Nanoc::Int::ItemRepRepo.new @view_context = Nanoc::ViewContext.new(reps: @reps, items: nil) @items = nil @item = nil @site = nil @config = nil end def test_xml_sitemap if_have 'builder', 'nokogiri' do # Create items @items = Nanoc::Int::IdentifiableCollection.new({}) # Create item 1 item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 1', {}, '/item-one/'), @view_context) @items << item create_item_rep(item.unwrap, :one_a, '/item-one/a/') create_item_rep(item.unwrap, :one_b, '/item-one/b/') # Create item 2 item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 2', { is_hidden: true }, '/item-two/'), @view_context) @items << item # Create item 3 attrs = { mtime: Time.parse('2004-07-12'), changefreq: 'daily', priority: 0.5 } item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 3', attrs, '/item-three/'), @view_context) @items << item create_item_rep(item.unwrap, :three_a, '/item-three/a/') create_item_rep(item.unwrap, :three_b, '/item-three/b/') # Create item 4 item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 4', {}, '/item-four/'), @view_context) @items << item create_item_rep(item.unwrap, :four_a, nil) # Create sitemap item @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('sitemap content', {}, '/sitemap/'), @view_context) # Create site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Build sitemap res = xml_sitemap # Check doc = Nokogiri::XML(res) urlsets = doc.css('> urlset') assert_equal 1, urlsets.size urls = urlsets.css('> url') assert_equal 4, urls.size assert_equal 'http://example.com/item-one/a/', urls[0].css('> loc').inner_text assert_equal 'http://example.com/item-one/b/', urls[1].css('> loc').inner_text assert_equal 'http://example.com/item-three/a/', urls[2].css('> loc').inner_text assert_equal 'http://example.com/item-three/b/', urls[3].css('> loc').inner_text assert_equal '', urls[0].css('> changefreq').inner_text assert_equal '', urls[1].css('> changefreq').inner_text assert_equal 'daily', urls[2].css('> changefreq').inner_text assert_equal 'daily', urls[3].css('> changefreq').inner_text assert_equal '', urls[0].css('> priority').inner_text assert_equal '', urls[1].css('> priority').inner_text assert_equal '0.5', urls[2].css('> priority').inner_text assert_equal '0.5', urls[3].css('> priority').inner_text assert_equal '', urls[0].css('> lastmod').inner_text assert_equal '', urls[1].css('> lastmod').inner_text assert_equal '2004-07-12', urls[2].css('> lastmod').inner_text assert_equal '2004-07-12', urls[3].css('> lastmod').inner_text end end def test_sitemap_with_items_as_param if_have 'builder', 'nokogiri' do # Create items @items = Nanoc::Int::IdentifiableCollection.new({}) @items << nil item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 1', {}, '/item-one/'), @view_context) @items << item create_item_rep(item.unwrap, :one_a, '/item-one/a/') create_item_rep(item.unwrap, :one_b, '/item-one/b/') @items << nil # Create sitemap item @item = Nanoc::Int::Item.new('sitemap content', {}, '/sitemap/') # Create site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Build sitemap res = xml_sitemap(items: [item]) # Check doc = Nokogiri::XML(res) urlsets = doc.css('> urlset') assert_equal 1, urlsets.size urls = urlsets.css('> url') assert_equal 2, urls.size assert_equal 'http://example.com/item-one/a/', urls[0].css('> loc').inner_text assert_equal 'http://example.com/item-one/b/', urls[1].css('> loc').inner_text assert_equal '', urls[0].css('> changefreq').inner_text assert_equal '', urls[1].css('> changefreq').inner_text assert_equal '', urls[0].css('> priority').inner_text assert_equal '', urls[1].css('> priority').inner_text assert_equal '', urls[0].css('> lastmod').inner_text assert_equal '', urls[1].css('> lastmod').inner_text end end def test_filter if_have 'builder', 'nokogiri' do # Create items @items = Nanoc::Int::IdentifiableCollection.new({}) item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 1', {}, '/item-one/'), @view_context) @items << item create_item_rep(item.unwrap, :one_a, '/item-one/a/') create_item_rep(item.unwrap, :one_b, '/item-one/b/') # Create sitemap item @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('sitemap content', {}, '/sitemap/'), @view_context) # Create site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Build sitemap res = xml_sitemap(rep_select: ->(rep) { rep.name == :one_a }) # Check doc = Nokogiri::XML(res) urlsets = doc.css('> urlset') assert_equal 1, urlsets.size urls = urlsets.css('> url') assert_equal 1, urls.size assert_equal 'http://example.com/item-one/a/', urls[0].css('> loc').inner_text assert_equal '', urls[0].css('> changefreq').inner_text assert_equal '', urls[0].css('> priority').inner_text assert_equal '', urls[0].css('> lastmod').inner_text end end def test_sorted if_have 'builder', 'nokogiri' do # Create items @items = Nanoc::Int::IdentifiableCollection.new({}) item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 1', {}, '/george/'), @view_context) @items << item create_item_rep(item.unwrap, :a_alice, '/george/alice/') create_item_rep(item.unwrap, :b_zoey, '/george/zoey/') item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 1', {}, '/walton/'), @view_context) @items << item create_item_rep(item.unwrap, :a_eve, '/walton/eve/') create_item_rep(item.unwrap, :b_bob, '/walton/bob/') item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('some content 1', {}, '/lucas/'), @view_context) @items << item create_item_rep(item.unwrap, :a_trudy, '/lucas/trudy/') create_item_rep(item.unwrap, :b_mallory, '/lucas/mallory/') # Create sitemap item @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('sitemap content', {}, '/sitemap/'), @view_context) # Create site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Build sitemap res = xml_sitemap(items: @items) # Check doc = Nokogiri::XML(res) urlsets = doc.css('> urlset') assert_equal 1, urlsets.size urls = urlsets.css('> url') assert_equal 6, urls.size assert_equal 'http://example.com/george/alice/', urls[0].css('> loc').inner_text assert_equal 'http://example.com/george/zoey/', urls[1].css('> loc').inner_text assert_equal 'http://example.com/lucas/trudy/', urls[2].css('> loc').inner_text assert_equal 'http://example.com/lucas/mallory/', urls[3].css('> loc').inner_text assert_equal 'http://example.com/walton/eve/', urls[4].css('> loc').inner_text assert_equal 'http://example.com/walton/bob/', urls[5].css('> loc').inner_text end end protected def create_item_rep(item, name, path) rep = Nanoc::Int::ItemRep.new(item, name) rep.paths = { last: path } rep.raw_paths = { last: path } @reps << rep rep end end nanoc-4.1.4/test/helpers/test_rendering.rb0000644000004100000410000001010712665031555020637 0ustar www-datawww-dataclass Nanoc::Helpers::RenderingTest < Nanoc::TestCase include Nanoc::Helpers::Rendering def test_render with_site do |site| File.open('Rules', 'w') do |io| io.write("layout '/foo/', :erb\n") end File.open('layouts/foo.erb', 'w') do |io| io.write 'This is the <%= @layout.identifier %> layout.' end site = Nanoc::Int::SiteLoader.new.new_from_cwd @site = Nanoc::SiteView.new(site, nil) @layouts = Nanoc::LayoutCollectionView.new(site.layouts, nil) assert_equal('This is the /foo/ layout.', render('/foo/')) end end def test_render_with_non_cleaned_identifier with_site do |site| File.open('Rules', 'w') do |io| io.write("layout '/foo/', :erb\n") end File.open('layouts/foo.erb', 'w') do |io| io.write 'This is the <%= @layout.identifier %> layout.' end site = Nanoc::Int::SiteLoader.new.new_from_cwd @site = Nanoc::SiteView.new(site, nil) @layouts = Nanoc::LayoutCollectionView.new(site.layouts, nil) assert_equal('This is the /foo/ layout.', render('/foo')) end end def test_render_class with_site do |site| File.open('Rules', 'w') do |io| io.write("layout '/foo/', :erb\n") end File.open('layouts/foo.erb', 'w') do |io| io.write 'I am the <%= @layout.class %> class.' end site = Nanoc::Int::SiteLoader.new.new_from_cwd @site = Nanoc::SiteView.new(site, nil) @layouts = Nanoc::LayoutCollectionView.new(site.layouts, nil) assert_equal('I am the Nanoc::LayoutView class.', render('/foo/')) end end def test_render_wrapped_class with_site do |site| File.open('Rules', 'w') do |io| io.write("layout '/foo/', :erb\n") end File.open('layouts/foo.erb', 'w') do |io| io.write 'I am the <%= @layout.unwrap.class %> class.' end site = Nanoc::Int::SiteLoader.new.new_from_cwd @site = Nanoc::SiteView.new(site, nil) @layouts = Nanoc::LayoutCollectionView.new(site.layouts, nil) assert_equal('I am the Nanoc::Int::Layout class.', render('/foo/')) end end def test_render_with_unknown_layout with_site do |site| site = Nanoc::Int::SiteLoader.new.new_from_cwd @site = Nanoc::SiteView.new(site, nil) @layouts = Nanoc::LayoutCollectionView.new(site.layouts, nil) assert_raises(Nanoc::Int::Errors::UnknownLayout) do render '/dsfghjkl/' end end end def test_render_without_filter with_site do |site| File.open('Rules', 'w') do |io| io.write("layout '/foo/', nil\n") end File.open('layouts/foo.erb', 'w').close site = Nanoc::Int::SiteLoader.new.new_from_cwd @site = Nanoc::SiteView.new(site, nil) @layouts = Nanoc::LayoutCollectionView.new(site.layouts, nil) assert_raises(Nanoc::Int::Errors::CannotDetermineFilter) do render '/foo/' end end end def test_render_with_unknown_filter with_site do |site| File.open('Rules', 'w') do |io| io.write("layout '/foo/', :asdf\n") end File.open('layouts/foo.erb', 'w').close site = Nanoc::Int::SiteLoader.new.new_from_cwd @site = Nanoc::SiteView.new(site, nil) @layouts = Nanoc::LayoutCollectionView.new(site.layouts, nil) assert_raises(Nanoc::Int::Errors::UnknownFilter) do render '/foo/' end end end def test_render_with_block with_site do |site| File.open('Rules', 'w') do |io| io.write("layout '/foo/', :erb\n") end File.open('layouts/foo.erb', 'w') do |io| io.write '[partial-before]<%= yield %>[partial-after]' end site = Nanoc::Int::SiteLoader.new.new_from_cwd @site = Nanoc::SiteView.new(site, nil) @layouts = Nanoc::LayoutCollectionView.new(site.layouts, nil) _erbout = '[erbout-before]' result = render '/foo/' do _erbout << 'This is some extra content' end assert_equal('[erbout-before][partial-before]This is some extra content[partial-after]', _erbout) assert_equal '', result end end end nanoc-4.1.4/test/helpers/test_blogging.rb0000644000004100000410000004331212665031555020456 0ustar www-datawww-dataclass Nanoc::Helpers::BloggingTest < Nanoc::TestCase include Nanoc::Helpers::Blogging include Nanoc::Helpers::Text def mock_article item = mock item.stubs(:[]).with(:updated_at).returns(Time.now - 500) item.stubs(:[]).with(:kind).returns('article') item.stubs(:[]).with(:created_at).returns(Time.now - 1000) item.stubs(:[]).with(:title).returns('An Item') item.stubs(:[]).with(:custom_path_in_feed).returns(nil) item.stubs(:[]).with(:custom_url_in_feed).returns(nil) item.stubs(:[]).with(:excerpt).returns(nil) item.stubs(:path).returns('/item/') item.stubs(:[]).with(:author_name).returns(nil) item.stubs(:[]).with(:author_uri).returns(nil) item.stubs(:compiled_content).returns('item content') item end def mock_item item = mock item.stubs(:[]).with(:kind).returns('item') item end def test_atom_feed if_have 'builder' do # Create items @items = [mock, mock_article, mock_article] # Create item 0 @items[0].stubs(:[]).with(:kind).returns('item') # Create item 1 @items[1].stubs(:[]).with(:updated_at).returns(Date.today - 1) @items[1].stubs(:[]).with(:kind).returns('article') @items[1].stubs(:[]).with(:created_at).returns((Date.today - 2).to_s) @items[1].stubs(:[]).with(:title).returns('Item One') @items[1].stubs(:[]).with(:custom_path_in_feed).returns(nil) @items[1].stubs(:[]).with(:custom_url_in_feed).returns(nil) @items[1].stubs(:[]).with(:excerpt).returns(nil) @items[1].stubs(:path).returns('/item1/') @items[1].expects(:compiled_content).with(snapshot: :pre).returns('item 1 content') # Create item 2 @items[2].expects(:compiled_content).with(snapshot: :pre).returns('item 2 content') # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Cool Blog') @item.stubs(:[]).with(:author_name).returns('Denis Defreyne') @item.stubs(:[]).with(:author_uri).returns('http://stoneship.org/') @item.stubs(:[]).with(:feed_url).returns(nil) @item.stubs(:path).returns('/journal/feed/') # Check atom_feed end end def test_atom_feed_with_times if_have 'builder' do # Create items @items = [mock_item, mock_article, mock_article] # Create item 1 @items[1].stubs(:[]).with(:updated_at).returns(Time.now - 500) @items[1].stubs(:[]).with(:created_at).returns(Time.now - 1000) @items[1].expects(:compiled_content).returns('item 1 content') # Create item 2 @items[2].stubs(:[]).with(:updated_at).returns(Time.now - 250) @items[2].stubs(:[]).with(:created_at).returns(Time.now - 1200) @items[2].expects(:compiled_content).returns('item 2 content') # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Cool Blog') @item.stubs(:[]).with(:author_name).returns('Denis Defreyne') @item.stubs(:[]).with(:author_uri).returns('http://stoneship.org/') @item.stubs(:[]).with(:feed_url).returns(nil) @item.stubs(:path).returns('/journal/feed/') # Check atom_feed end end def test_atom_feed_without_articles if_have 'builder' do # Mock items @items = [mock_item, mock_item] # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') # Check error = assert_raises(Nanoc::Int::Errors::GenericTrivial) do atom_feed end assert_equal( 'Cannot build Atom feed: no articles', error.message, ) end end def test_atom_feed_without_base_url if_have 'builder' do # Create items @items = [mock_item, mock_article] # Mock site @config = Nanoc::ConfigView.new({ base_url: nil }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') # Check error = assert_raises(Nanoc::Int::Errors::GenericTrivial) do atom_feed end assert_equal( 'Cannot build Atom feed: site configuration has no base_url', error.message, ) end end def test_atom_feed_without_title if_have 'builder' do # Create items @items = [mock_item, mock_article] # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns(nil) @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') # Check error = assert_raises(Nanoc::Int::Errors::GenericTrivial) do atom_feed end assert_equal( 'Cannot build Atom feed: no title in params, item or site config', error.message, ) end end def test_atom_feed_without_author_name if_have 'builder' do # Create items @items = [mock_item, mock_article] # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns(nil) @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') # Check error = assert_raises(Nanoc::Int::Errors::GenericTrivial) do atom_feed end assert_equal( 'Cannot build Atom feed: no author_name in params, item or site config', error.message, ) end end def test_atom_feed_with_author_name_and_uri_from_content_item if_have 'builder' do # Create items @items = [mock_article] # Create item 1 @items[0].stubs(:[]).with(:author_name).returns('Don Alias') @items[0].stubs(:[]).with(:author_uri).returns('http://don.example.com/') @items[0].expects(:compiled_content).returns('item 1 content') # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com/' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:kind).returns(nil) @item.stubs(:[]).with(:title).returns('My Cool Blog') @item.stubs(:[]).with(:author_name).returns('Denis Defreyne') @item.stubs(:[]).with(:author_uri).returns('http://stoneship.org/') @item.stubs(:[]).with(:feed_url).returns(nil) @item.stubs(:path).returns('/journal/feed/') # Check # TODO: Use xpath matchers for more specific test result = atom_feed # Still should keep feed level author assert_match( /#{Regexp.escape('Denis Defreyne')}/, #' result, ) assert_match( /#{Regexp.escape('http://stoneship.org/')}/, #' result, ) # Overrides on specific items assert_match( /#{Regexp.escape('Don Alias')}/, #' result, ) assert_match( /#{Regexp.escape('http://don.example.com/')}/, #' result, ) end end def test_atom_feed_without_author_uri if_have 'builder' do # Create items @items = [mock_item, mock_article] # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns(nil) # Check error = assert_raises(Nanoc::Int::Errors::GenericTrivial) do atom_feed end assert_equal( 'Cannot build Atom feed: no author_uri in params, item or site config', error.message, ) end end def test_atom_feed_without_articles_created_at if_have 'builder' do # Create items @items = [mock_item, mock_article, mock_article] @items[1].stubs(:[]).with(:created_at).returns(Time.now.to_s) @items[2].stubs(:[]).with(:created_at).returns(nil) # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') # Check error = assert_raises(Nanoc::Int::Errors::GenericTrivial) do atom_feed end assert_equal( 'Cannot build Atom feed: one or more articles lack created_at', error.message, ) end end def test_atom_feed_with_title_author_name_and_uri_as_params if_have 'builder' do # Create items @items = [mock_item, mock_article] @items[1].expects(:compiled_content).with(snapshot: :pre).returns('asdf') # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns(nil) @item.stubs(:[]).with(:author_name).returns(nil) @item.stubs(:[]).with(:author_uri).returns(nil) @item.stubs(:[]).with(:[]).with(:feed_url).returns('http://example.com/feed') # Check atom_feed( author_name: 'Bob', author_uri: 'http://example.com/~bob/', title: 'My Blog Or Something', ) end end def test_atom_feed_with_title_author_name_and_uri_from_config if_have 'builder' do # Create items @items = [mock_item, mock_article] @items[1].expects(:compiled_content).with(snapshot: :pre).returns('asdf') # Mock site @config = Nanoc::ConfigView.new( { author_name: 'Bob', author_uri: 'http://example.com/~bob/', title: 'My Blog Or Something', base_url: 'http://example.com', }, nil, ) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns(nil) @item.stubs(:[]).with(:author_name).returns(nil) @item.stubs(:[]).with(:author_uri).returns(nil) @item.stubs(:[]).with(:[]).with(:feed_url).returns('http://example.com/feed') # Check atom_feed end end def test_atom_feed_with_articles_param if_have 'builder' do # Mock items @items = [mock_article, mock_article] @items[0].expects(:compiled_content).never @items[1].stubs(:[]).with(:title).returns('Item One') @items[1].expects(:compiled_content).with(snapshot: :pre).returns('asdf') # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') @item.stubs(:[]).with(:[]).with(:feed_url).returns('http://example.com/feed') # Check atom_feed articles: [@items[1]] end end def test_atom_feed_with_limit_param if_have 'builder' do # Mock articles @items = [mock_article, mock_article] @items.each_with_index do |article, i| article.stubs(:[]).with(:title).returns("Article #{i}") article.stubs(:[]).with(:created_at).returns(Time.now - i) end # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') @item.stubs(:[]).with(:feed_url).returns('http://example.com/feed') # Check result = atom_feed limit: 1, articles: @items assert_match( Regexp.new('Article 0', Regexp::MULTILINE), result, ) refute_match( Regexp.new('Article 1', Regexp::MULTILINE), result, ) end end def test_atom_feed_sorting if_have 'builder' do # Mock articles @items = [mock_article, mock_article] @items.each_with_index do |article, i| article.stubs(:[]).with(:title).returns("Article #{i}") end @items[0].stubs(:[]).with(:created_at).returns('23-02-2009') @items[1].stubs(:[]).with(:created_at).returns('22-03-2009') # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') @item.stubs(:[]).with(:feed_url).returns('http://example.com/feed') # Check result = atom_feed assert_match( Regexp.new('Article 1.*Article 0', Regexp::MULTILINE), result, ) end end def test_atom_feed_preserve_order if_have 'builder' do # Mock articles @items = [mock_article, mock_article] @items.each_with_index do |article, i| article.stubs(:[]).with(:title).returns("Article #{i}") end @items[0].stubs(:[]).with(:created_at).returns('01-01-2015') @items[1].stubs(:[]).with(:created_at).returns('01-01-2014') # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') @item.stubs(:[]).with(:feed_url).returns('http://example.com/feed') # Check result = atom_feed(preserve_order: true) assert_match( Regexp.new('Article 1.*Article 0', Regexp::MULTILINE), result, ) end end def test_atom_feed_with_content_proc_param if_have 'builder' do # Mock article @items = [mock_article] # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') @item.stubs(:[]).with(:feed_url).returns('http://example.com/feed') # Check result = atom_feed content_proc: ->(_a) { 'foobar!' } assert_match 'foobar!', result end end def test_atom_feed_with_excerpt_proc_param if_have 'builder' do # Mock article @items = [mock_article] # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') @item.stubs(:[]).with(:[]).with(:feed_url).returns('http://example.com/feed') # Check result = atom_feed excerpt_proc: ->(_a) { 'foobar!' } assert_match 'foobar!', result end end def test_atom_feed_with_icon_param if_have 'builder' do # Mock article @items = [mock_article] # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') @item.stubs(:[]).with(:feed_url).returns('http://example.com/feed') # Check result = atom_feed icon: 'http://example.com/icon.png' assert_match 'http://example.com/icon.png', result end end def test_atom_feed_with_logo_param if_have 'builder' do # Mock article @items = [mock_article] # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:[]).with(:title).returns('My Blog Or Something') @item.stubs(:[]).with(:author_name).returns('J. Doe') @item.stubs(:[]).with(:author_uri).returns('http://example.com/~jdoe') @item.stubs(:[]).with(:feed_url).returns('http://example.com/feed') # Check result = atom_feed logo: 'http://example.com/logo.png' assert_match 'http://example.com/logo.png', result end end def test_atom_feed_with_item_without_path if_have 'builder' do # Create items @items = [mock_article] @items[0].stubs(:path).returns(nil) # Mock site @config = Nanoc::ConfigView.new({ base_url: 'http://example.com' }, nil) # Create feed item @item = mock @item.stubs(:identifier).returns('/feed/') @item.stubs(:[]).with(:title).returns('My Cool Blog') @item.stubs(:[]).with(:author_name).returns('Denis Defreyne') @item.stubs(:[]).with(:author_uri).returns('http://stoneship.org/') @item.stubs(:[]).with(:feed_url).returns(nil) @item.stubs(:path).returns('/journal/feed/') # Check atom_feed end end end nanoc-4.1.4/test/helpers/test_html_escape.rb0000644000004100000410000000117412665031555021152 0ustar www-datawww-dataclass Nanoc::Helpers::HTMLEscapeTest < Nanoc::TestCase include Nanoc::Helpers::HTMLEscape def test_html_escape_with_string assert_equal('<', html_escape('<')) assert_equal('>', html_escape('>')) assert_equal('&', html_escape('&')) assert_equal('"', html_escape('"')) end def test_html_escape_with_block _erbout = 'moo' html_escape do _erbout << '

Looks like a header

' end assert_equal 'moo<h1>Looks like a header</h1>', _erbout end def test_html_escape_without_string_or_block assert_raises RuntimeError do h end end end nanoc-4.1.4/test/helpers/test_capturing.rb0000644000004100000410000002627712665031555020675 0ustar www-datawww-dataclass Nanoc::Helpers::CapturingTest < Nanoc::TestCase include Nanoc::Helpers::Capturing def test_content_for require 'erb' File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end # Build content to be evaluated content = "head <% content_for :sidebar do %>\n" \ " <%= 1+2 %>\n" \ '<% end %> foot' # Build site site = Nanoc::Int::SiteLoader.new.new_empty item = Nanoc::Int::Item.new('moo', {}, '/blah/') @site = Nanoc::SiteView.new(Nanoc::Int::SiteLoader.new.new_empty, nil) @item = Nanoc::ItemWithRepsView.new(item, nil) # Evaluate content result = ::ERB.new(content).result(binding) # Check assert_equal '3', content_for(@item, :sidebar).strip assert_match(/^head\s+foot$/, result) end def test_capture require 'erb' # Build site @site = Nanoc::SiteView.new(Nanoc::Int::SiteLoader.new.new_empty, nil) @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('moo', {}, '/blah/'), nil) # Capture _erbout = 'foo' captured_content = capture do _erbout << 'bar' end # Check assert_equal 'foo', _erbout assert_equal 'bar', captured_content end def test_content_for_recursively require 'erb' File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end content = < basic <% end %> <% content_for :outerbox do %> before <%= content_for @item, :box %> after <% end %> <%= content_for @item, :outerbox %> foot EOS @site = Nanoc::SiteView.new(Nanoc::Int::SiteLoader.new.new_empty, nil) @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('content', {}, '/'), nil) result = ::ERB.new(content).result(binding) expected = %w( head before basic after foot ) actual = result.scan(/[a-z]+/) assert_equal expected, actual end def test_different_sites require 'erb' File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end @site = Nanoc::SiteView.new(Nanoc::Int::SiteLoader.new.new_empty, nil) @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('content', {}, '/'), nil) content = '<% content_for :a do %>Content One<% end %>' ::ERB.new(content).result(binding) assert_equal 'Content One', content_for(@item, :a) assert_equal nil, content_for(@item, :b) @site = Nanoc::SiteView.new(Nanoc::Int::SiteLoader.new.new_empty, nil) @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('content', {}, '/'), nil) content = '<% content_for :b do %>Content Two<% end %>' ::ERB.new(content).result(binding) assert_equal nil, content_for(@item, :a) assert_equal 'Content Two', content_for(@item, :b) end def test_content_for_with_existing_symbol with_site do |_site| # Prepare File.open('lib/helpers.rb', 'w') do |io| io.write 'include Nanoc::Helpers::Capturing' end File.open('content/includer.erb', 'w') do |io| io.write '[<%= content_for(@items["/includee/"], :blah) %>]' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>First content<% end %><% content_for :blah do %>Second content<% end %>}' end # Using the same symbols twice now raises an error, to be changed to concatenating in a future version assert_raises do Nanoc::CLI.run(%w(compile)) end end end def test_content_for_with_existing_symbol_with_error_option with_site do |_site| # Prepare File.open('lib/helpers.rb', 'w') do |io| io.write 'include Nanoc::Helpers::Capturing' end File.open('content/includer.erb', 'w') do |io| io.write '[<%= content_for(@items["/includee/"], :blah) %>]' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>First content<% end %><% content_for :blah, existing: :error do %>Second content<% end %>}' end assert_raises do Nanoc::CLI.run(%w(compile)) end end end def test_content_for_with_existing_symbol_with_overwrite_option with_site do |_site| # Prepare File.open('lib/helpers.rb', 'w') do |io| io.write 'include Nanoc::Helpers::Capturing' end File.open('content/includer.erb', 'w') do |io| io.write '[<%= content_for(@items["/includee/"], :blah) %>]' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>First content<% end %><% content_for :blah, existing: :overwrite do %>Second content<% end %>}' end Nanoc::CLI.run(%w(compile)) assert_equal '[Second content]', File.read('output/includer/index.html') end end def test_content_for_with_existing_symbol_with_append_option with_site do |_site| # Prepare File.open('lib/helpers.rb', 'w') do |io| io.write 'include Nanoc::Helpers::Capturing' end File.open('content/includer.erb', 'w') do |io| io.write '[<%= content_for(@items["/includee/"], :blah) %>]' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>First content<% end %><% content_for :blah, existing: :append do %>Second content<% end %>}' end Nanoc::CLI.run(%w(compile)) assert_equal '[First contentSecond content]', File.read('output/includer/index.html') end end def test_content_for_with_existing_symbol_with_unrecognised_option with_site do |_site| # Prepare File.open('lib/helpers.rb', 'w') do |io| io.write 'include Nanoc::Helpers::Capturing' end File.open('content/includer.erb', 'w') do |io| io.write '[<%= content_for(@items["/includee/"], :blah) %>]' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah, existing: :donkey do %>First content<% end %>}' end assert_raises(ArgumentError) do Nanoc::CLI.run(%w(compile)) end end end def test_dependencies with_site do |_site| # Prepare File.open('lib/helpers.rb', 'w') do |io| io.write 'include Nanoc::Helpers::Capturing' end File.open('content/includer.erb', 'w') do |io| io.write '[<%= content_for(@items.find { |i| i.identifier == \'/includee/\' }, :blah) %>]' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end # Compile once File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>Old content<% end %>}' end Nanoc::CLI.run(%w(compile)) assert_equal '[Old content]', File.read('output/includer/index.html') # Compile again File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>New content<% end %>}' end Nanoc::CLI.run(%w(compile)) assert_equal '[New content]', File.read('output/includer/index.html') end end def test_dependency_without_item_variable with_site do |_site| # Prepare File.open('lib/helpers.rb', 'w') do |io| io.write "include Nanoc::Helpers::Capturing\n" io.write "include Nanoc::Helpers::Rendering\n" end File.open('content/includer.erb', 'w') do |io| io.write '{<%= render \'partial\', :item => nil %>}' end File.open('layouts/partial.erb', 'w') do |io| io.write '[<%= @item.inspect %>-<%= content_for(@items.find { |i| i.identifier == \'/includee/\' }, :blah) %>]' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" io.write "layout '*', :erb\n" end # Compile once File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>Old content<% end %>}' end Nanoc::CLI.run(%w(compile)) assert_equal '{[nil-Old content]}', File.read('output/includer/index.html') # Compile again File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>New content<% end %>}' end Nanoc::CLI.run(%w(compile)) assert_equal '{[nil-New content]}', File.read('output/includer/index.html') end end def test_self with_site do |_site| File.open('lib/helpers.rb', 'w') do |io| io.write 'include Nanoc::Helpers::Capturing' end File.open('content/self.erb', 'w') do |io| io.write '<% content_for :foo do %>Foo!<% end %>' io.write '<%= content_for(@item, :foo) %>' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end Nanoc::CLI.run(%w(compile)) assert_equal 'Foo!', File.read('output/self/index.html') end end def test_recompile_dependency with_site do |_site| # Prepare File.open('lib/helpers.rb', 'w') do |io| io.write 'include Nanoc::Helpers::Capturing' end File.open('content/includee.erb', 'w') do |io| io.write '{<% content_for :blah do %>Content<% end %>}' end File.open('Rules', 'w') do |io| io.write "compile '*' do ; filter :erb ; end\n" io.write "route '*' do ; item.identifier + 'index.html' ; end\n" end # Compile once File.open('content/includer.erb', 'w') do |io| io.write 'Old-<%= content_for(@items.find { |i| i.identifier == \'/includee/\' }, :blah) %>' end Nanoc::CLI.run(%w(compile)) assert_equal '{}', File.read('output/includee/index.html') assert_equal 'Old-Content', File.read('output/includer/index.html') # Compile again File.open('content/includer.erb', 'w') do |io| io.write 'New-<%= content_for(@items.find { |i| i.identifier == \'/includee/\' }, :blah) %>' end Nanoc::CLI.run(%w(compile)) assert_equal '{}', File.read('output/includee/index.html') assert_equal 'New-Content', File.read('output/includer/index.html') end end end nanoc-4.1.4/test/helpers/test_link_to.rb0000644000004100000410000001333012665031555020322 0ustar www-datawww-dataclass Nanoc::Helpers::LinkToTest < Nanoc::TestCase include Nanoc::Helpers::LinkTo def test_link_to_with_path assert_equal( 'Foo', link_to('Foo', '/foo/'), ) end def test_link_to_with_item_without_reps_view target = Nanoc::ItemWithoutRepsView.new(mock, {}) target.stubs(:path).returns('/bar/') assert_equal( 'Bar', link_to('Bar', target), ) end def test_link_to_with_item_with_reps_view target = Nanoc::ItemWithRepsView.new(mock, {}) target.stubs(:path).returns('/bar/') assert_equal( 'Bar', link_to('Bar', target), ) end def test_link_to_with_item_rep_view target = Nanoc::ItemRepView.new(mock, {}) target.stubs(:path).returns('/bar/') assert_equal( 'Bar', link_to('Bar', target), ) end def test_link_to_with_attributes # Check assert_equal( 'Foo', link_to('Foo', '/foo/', title: 'Dis mai foo!'), ) end def test_link_to_escape # Check assert_equal( 'Foo & Bar', link_to('Foo & Bar', '/foo&bar/', title: 'Foo & Bar'), ) end def test_link_to_to_nil_item_or_item_rep target = Nanoc::ItemRepView.new(mock, {}) target.stubs(:path).returns(nil) assert_raises RuntimeError do link_to('Some Text', target) end end def test_link_to_unless_current_current # Create item @item_rep = mock @item_rep.stubs(:path).returns('/foo/') # Check assert_equal( 'Bar', link_to_unless_current('Bar', @item_rep), ) ensure @item = nil end def test_link_to_unless_current_not_current # Create item @item_rep = mock @item_rep.stubs(:path).returns('/foo/') # Check assert_equal( 'Bar', link_to_unless_current('Bar', '/abc/xyz/'), ) end def test_relative_path_to_with_self # Mock item @item_rep = mock @item_rep.stubs(:path).returns('/foo/bar/baz/') # Test assert_equal( './', relative_path_to('/foo/bar/baz/'), ) end def test_relative_path_to_with_root # Mock item @item_rep = mock @item_rep.stubs(:path).returns('/foo/bar/baz/') # Test assert_equal( '../../../', relative_path_to('/'), ) end def test_relative_path_to_file # Mock item @item_rep = mock @item_rep.stubs(:path).returns('/foo/bar/baz/') # Test assert_equal( '../../quux', relative_path_to('/foo/quux'), ) end def test_relative_path_to_dir # Mock item @item_rep = mock @item_rep.stubs(:path).returns('/foo/bar/baz/') # Test assert_equal( '../../quux/', relative_path_to('/foo/quux/'), ) end def test_relative_path_to_rep # Mock self @item_rep = mock @item_rep.stubs(:path).returns('/foo/bar/baz/') # Mock other other_item_rep = mock other_item_rep.stubs(:path).returns('/foo/quux/') # Test assert_equal( '../../quux/', relative_path_to(other_item_rep), ) end def test_relative_path_to_item # Mock self @item_rep = mock @item_rep.stubs(:path).returns('/foo/bar/baz/') # Mock other other_item = mock other_item.stubs(:path).returns('/foo/quux/') # Test assert_equal( '../../quux/', relative_path_to(other_item), ) end def test_relative_path_to_to_nil # Mock self @item_rep = mock @item_rep.stubs(:path).returns(nil) # Mock other other_item_rep = mock other_item_rep.stubs(:path).returns('/foo/quux/') # Test assert_raises RuntimeError do relative_path_to(other_item_rep) end end def test_relative_path_to_from_nil # Mock self @item_rep = mock @item_rep.stubs(:path).returns('/foo/quux/') # Mock other other_item_rep = mock other_item_rep.stubs(:path).returns(nil) # Test assert_raises RuntimeError do relative_path_to(other_item_rep) end end def test_relative_path_to_to_windows_path @item_rep = mock @item_rep.stubs(:path).returns('/foo/quux/') assert_equal '//mydomain/tahontaenrat', relative_path_to('//mydomain/tahontaenrat') end def test_examples_link_to # Parse YARD.parse(LIB_DIR + '/nanoc/helpers/link_to.rb') # Mock @items = [ Nanoc::ItemRepView.new(mock, {}), Nanoc::ItemRepView.new(mock, {}), Nanoc::ItemRepView.new(mock, {}), ] @items[0].stubs(:identifier).returns('/about/') @items[0].stubs(:path).returns('/about.html') @items[1].stubs(:identifier).returns('/software/') @items[1].stubs(:path).returns('/software.html') @items[2].stubs(:identifier).returns('/software/nanoc/') @items[2].stubs(:path).returns('/software/nanoc.html') about_rep_vcard = Nanoc::ItemRepView.new(mock, {}) about_rep_vcard.stubs(:path).returns('/about.vcf') @items[0].stubs(:rep).with(:vcard).returns(about_rep_vcard) # Run assert_examples_correct 'Nanoc::Helpers::LinkTo#link_to' end def test_examples_link_to_unless_current # Parse YARD.parse(LIB_DIR + '/nanoc/helpers/link_to.rb') # Mock @item_rep = mock @item_rep.stubs(:path).returns('/about/') @item = mock @item.stubs(:path).returns(@item_rep.path) # Run assert_examples_correct 'Nanoc::Helpers::LinkTo#link_to_unless_current' end def test_examples_relative_path_to # Parse YARD.parse(LIB_DIR + '/nanoc/helpers/link_to.rb') # Mock @item_rep = mock @item_rep.stubs(:path).returns('/foo/bar/') # Run assert_examples_correct 'Nanoc::Helpers::LinkTo#relative_path_to' end end nanoc-4.1.4/test/data_sources/0000755000004100000410000000000012665031555016311 5ustar www-datawww-datananoc-4.1.4/test/data_sources/test_filesystem_unified.rb0000644000004100000410000004413012665031555023566 0ustar www-datawww-dataclass Nanoc::DataSources::FilesystemUnifiedTest < Nanoc::TestCase def new_data_source(params = nil) # Mock site site = Nanoc::Int::SiteLoader.new.new_empty # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(site.config, nil, nil, params) # Done data_source end def test_load_objects # Create data source data_source = new_data_source # Create a fake class klass = Class.new do attr_reader :stuff def initialize(*stuff) @stuff = stuff end def ==(other) @stuff == other.stuff end end # Create sample files FileUtils.mkdir_p('foo') FileUtils.mkdir_p('foo/a/b') File.open('foo/bar.html', 'w') { |io| io.write("---\nnum: 1\n---\ntest 1") } File.open('foo/b.c.html', 'w') { |io| io.write("---\nnum: 2\n---\ntest 2") } File.open('foo/a/b/c.html', 'w') { |io| io.write("---\nnum: 3\n---\ntest 3") } File.open('foo/ugly.html~', 'w') { |io| io.write("---\nnum: 4\n---\ntest 4") } File.open('foo/ugly.html.orig', 'w') { |io| io.write("---\nnum: 5\n---\ntest 5") } File.open('foo/ugly.html.rej', 'w') { |io| io.write("---\nnum: 6\n---\ntest 6") } File.open('foo/ugly.html.bak', 'w') { |io| io.write("---\nnum: 7\n---\ntest 7") } # Get expected and actual output expected_out = [ klass.new( 'test 1', { 'num' => 1, :filename => 'foo/bar.html', :extension => 'html', mtime: File.mtime('foo/bar.html') }, '/bar/', ), klass.new( 'test 2', { 'num' => 2, :filename => 'foo/b.c.html', :extension => 'c.html', mtime: File.mtime('foo/b.c.html') }, '/b/', ), klass.new( 'test 3', { 'num' => 3, :filename => 'foo/a/b/c.html', :extension => 'html', mtime: File.mtime('foo/a/b/c.html') }, '/a/b/c/', ), ] actual_out = data_source.send(:load_objects, 'foo', 'The Foo', klass).sort_by { |i| i.stuff[0].string } # Check (0..expected_out.size - 1).each do |i| assert_equal expected_out[i].stuff[0], actual_out[i].stuff[0].string, 'content must match' assert_equal expected_out[i].stuff[2], actual_out[i].stuff[2], 'identifier must match' ['num', :filename, :extension, :mtime].each do |key| assert_equal expected_out[i].stuff[1][key], actual_out[i].stuff[1][key], "attribute key #{key} must match" end end end def test_load_objects_with_same_extensions # Create data source data_source = new_data_source({ identifier_type: 'full' }) # Create a fake class klass = Class.new do attr_reader :stuff def initialize(*stuff) @stuff = stuff end def ==(other) @stuff == other.stuff end end # Create sample files FileUtils.mkdir_p('foo') File.open('foo/bar.html', 'w') { |io| io.write("---\nnum: 1\n---\ntest 1") } File.open('foo/bar.md', 'w') { |io| io.write("---\nnum: 1\n---\ntest 1") } # Check actual_out = data_source.send(:load_objects, 'foo', 'The Foo', klass) assert_equal 2, actual_out.size end def test_load_binary_objects # Create data source data_source = new_data_source # Create sample files FileUtils.mkdir_p('foo') File.open('foo/stuff.dat', 'w') { |io| io.write('random binary data') } # Load items = data_source.send(:load_objects, 'foo', 'item', Nanoc::Int::Item) # Check assert_equal 1, items.size assert items[0].content.binary? assert_equal "#{Dir.getwd}/foo/stuff.dat", items[0].content.filename assert_equal Nanoc::Int::BinaryContent, items[0].content.class end def test_load_layouts_with_nil_dir_name # Create data source data_source = new_data_source(layouts_dir: nil) # Create sample files FileUtils.mkdir_p('layouts') File.write('layouts/stuff.txt', 'blah blah') # Load layouts = data_source.layouts # Check assert_empty(layouts) end def test_load_binary_layouts # Create data source data_source = new_data_source # Create sample files FileUtils.mkdir_p('foo') File.open('foo/stuff.dat', 'w') { |io| io.write('random binary data') } # Load assert_raises(RuntimeError) do data_source.send(:load_objects, 'foo', 'item', Nanoc::Int::Layout) end end def test_identifier_for_filename_with_full_style_identifier # Create data source data_source = new_data_source({ identifier_type: 'full' }) # Get input and expected output expected = { '/foo' => Nanoc::Identifier.new('/foo', type: :full), '/foo.html' => Nanoc::Identifier.new('/foo.html', type: :full), '/foo/index.html' => Nanoc::Identifier.new('/foo/index.html', type: :full), '/foo.html.erb' => Nanoc::Identifier.new('/foo.html.erb', type: :full), } # Check expected.each_pair do |input, expected_output| actual_output = data_source.send(:identifier_for_filename, input) assert_equal( expected_output, actual_output, "identifier_for_filename(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_identifier_for_filename_allowing_periods_in_identifiers # Create data source data_source = new_data_source(allow_periods_in_identifiers: true) # Get input and expected output expected = { '/foo' => '/foo/', '/foo.html' => '/foo/', '/foo/index.html' => '/foo/', '/foo.entry.html' => '/foo.entry/', } # Check expected.each_pair do |input, expected_output| actual_output = data_source.send(:identifier_for_filename, input) assert_equal( expected_output, actual_output, "identifier_for_filename(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_identifier_for_filename_disallowing_periods_in_identifiers # Create data source data_source = new_data_source # Get input and expected output expected = { '/foo' => '/foo/', '/foo.html' => '/foo/', '/foo/index.html' => '/foo/', '/foo.html.erb' => '/foo/', } # Check expected.each_pair do |input, expected_output| actual_output = data_source.send(:identifier_for_filename, input) assert_equal( expected_output, actual_output, "identifier_for_filename(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_identifier_for_filename_with_subfilename_allowing_periods_in_identifiers expectations = { 'foo/bar.yaml' => '/foo/bar/', 'foo/quxbar.yaml' => '/foo/quxbar/', 'foo/barqux.yaml' => '/foo/barqux/', 'foo/quxbarqux.yaml' => '/foo/quxbarqux/', 'foo/qux.bar.yaml' => '/foo/qux.bar/', 'foo/bar.qux.yaml' => '/foo/bar.qux/', 'foo/qux.bar.qux.yaml' => '/foo/qux.bar.qux/', 'foo/index.yaml' => '/foo/', 'index.yaml' => '/', 'foo/blah_index.yaml' => '/foo/blah_index/', } data_source = new_data_source(allow_periods_in_identifiers: true) expectations.each_pair do |meta_filename, expected_identifier| content_filename = meta_filename.sub(/yaml$/, 'html') [meta_filename, content_filename].each do |filename| assert_equal( expected_identifier, data_source.instance_eval { identifier_for_filename(filename) }, ) end end end def test_identifier_for_filename_with_subfilename_disallowing_periods_in_identifiers expectations = { 'foo/bar.yaml' => '/foo/bar/', 'foo/quxbar.yaml' => '/foo/quxbar/', 'foo/barqux.yaml' => '/foo/barqux/', 'foo/quxbarqux.yaml' => '/foo/quxbarqux/', 'foo/qux.bar.yaml' => '/foo/qux/', 'foo/bar.qux.yaml' => '/foo/bar/', 'foo/qux.bar.qux.yaml' => '/foo/qux/', 'foo/index.yaml' => '/foo/', 'index.yaml' => '/', 'foo/blah_index.yaml' => '/foo/blah_index/', } data_source = new_data_source expectations.each_pair do |meta_filename, expected_identifier| content_filename = meta_filename.sub(/yaml$/, 'html') [meta_filename, content_filename].each do |filename| assert_equal( expected_identifier, data_source.instance_eval { identifier_for_filename(filename) }, ) end end end def test_identifier_for_filename_with_index_filenames_allowing_periods_in_identifier expected = { '/index.html.erb' => '/index.html/', '/index.html' => '/', '/index' => '/', '/foo/index.html.erb' => '/foo/index.html/', '/foo/index.html' => '/foo/', '/foo/index' => '/foo/', } data_source = new_data_source(allow_periods_in_identifiers: true) expected.each_pair do |input, expected_output| actual_output = data_source.send(:identifier_for_filename, input) assert_equal( expected_output, actual_output, "identifier_for_filename(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_identifier_for_filename_with_index_filenames_disallowing_periods_in_identifier expected = { '/index.html.erb' => '/', '/index.html' => '/', '/index' => '/', '/foo/index.html.erb' => '/foo/', '/foo/index.html' => '/foo/', '/foo/index' => '/foo/', } data_source = new_data_source expected.each_pair do |input, expected_output| actual_output = data_source.send(:identifier_for_filename, input) assert_equal( expected_output, actual_output, "identifier_for_filename(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_load_objects_allowing_periods_in_identifiers # Create data source data_source = new_data_source(allow_periods_in_identifiers: true) # Create a fake class klass = Class.new do attr_reader :stuff def initialize(*stuff) @stuff = stuff end def ==(other) @stuff == other.stuff end end # Create sample files FileUtils.mkdir_p('foo') FileUtils.mkdir_p('foo/a/b') File.open('foo/a/b/c.yaml', 'w') { |io| io.write("---\nnum: 1\n") } File.open('foo/b.c.yaml', 'w') { |io| io.write("---\nnum: 2\n") } File.open('foo/b.c.html', 'w') { |io| io.write('test 2') } File.open('foo/car.html', 'w') { |io| io.write('test 3') } File.open('foo/ugly.yaml~', 'w') { |io| io.write('blah') } File.open('foo/ugly.html~', 'w') { |io| io.write('blah') } File.open('foo/ugly.html.orig', 'w') { |io| io.write('blah') } File.open('foo/ugly.html.rej', 'w') { |io| io.write('blah') } File.open('foo/ugly.html.bak', 'w') { |io| io.write('blah') } # Get expected output expected_out = [ klass.new( '', { 'num' => 1, :content_filename => nil, :meta_filename => 'foo/a/b/c.yaml', :extension => nil, :file => nil, mtime: File.mtime('foo/a/b/c.yaml'), }, '/a/b/c/', ), klass.new( 'test 2', { 'num' => 2, :content_filename => 'foo/b.c.html', :meta_filename => 'foo/b.c.yaml', :extension => 'html', :file => File.open('foo/b.c.html'), mtime: File.mtime('foo/b.c.html') > File.mtime('foo/b.c.yaml') ? File.mtime('foo/b.c.html') : File.mtime('foo/b.c.yaml'), }, '/b.c/', ), klass.new( 'test 3', { content_filename: 'foo/car.html', meta_filename: nil, extension: 'html', file: File.open('foo/car.html'), mtime: File.mtime('foo/car.html'), }, '/car/', ), ] # Get actual output ordered by identifier actual_out = data_source.send(:load_objects, 'foo', 'The Foo', klass).sort_by { |i| i.stuff[2] } # Check (0..expected_out.size - 1).each do |i| assert_equal expected_out[i].stuff[0], actual_out[i].stuff[0].string, 'content must match' assert_equal expected_out[i].stuff[2], actual_out[i].stuff[2], 'identifier must match' ['num', :content_filename, :meta_filename, :extension, :mtime].each do |key| assert_equal expected_out[i].stuff[1][key], actual_out[i].stuff[1][key], "attribute key #{key} must match" end end end def test_load_objects_disallowing_periods_in_identifiers # Create data source data_source = new_data_source # Create a fake class klass = Class.new do attr_reader :stuff def initialize(*stuff) @stuff = stuff end def ==(other) @stuff == other.stuff end end # Create sample files FileUtils.mkdir_p('foo') FileUtils.mkdir_p('foo/a/b') File.open('foo/a/b/c.yaml', 'w') { |io| io.write("---\nnum: 1\n") } File.open('foo/b.yaml', 'w') { |io| io.write("---\nnum: 2\n") } File.open('foo/b.html.erb', 'w') { |io| io.write('test 2') } File.open('foo/car.html', 'w') { |io| io.write('test 3') } File.open('foo/ugly.yaml~', 'w') { |io| io.write('blah') } File.open('foo/ugly.html~', 'w') { |io| io.write('blah') } File.open('foo/ugly.html.orig', 'w') { |io| io.write('blah') } File.open('foo/ugly.html.rej', 'w') { |io| io.write('blah') } File.open('foo/ugly.html.bak', 'w') { |io| io.write('blah') } # Get expected output expected_out = [ klass.new( '', { 'num' => 1, :content_filename => nil, :meta_filename => 'foo/a/b/c.yaml', :extension => nil, :file => nil, mtime: File.mtime('foo/a/b/c.yaml'), }, '/a/b/c/', ), klass.new( 'test 2', { 'num' => 2, :content_filename => 'foo/b.html.erb', :meta_filename => 'foo/b.yaml', :extension => 'html.erb', :file => File.open('foo/b.html.erb'), mtime: File.mtime('foo/b.html.erb') > File.mtime('foo/b.yaml') ? File.mtime('foo/b.html.erb') : File.mtime('foo/b.yaml'), }, '/b/', ), klass.new( 'test 3', { content_filename: 'foo/car.html', meta_filename: nil, extension: 'html', file: File.open('foo/car.html'), mtime: File.mtime('foo/car.html'), }, '/car/', ), ] # Get actual output ordered by identifier actual_out = data_source.send(:load_objects, 'foo', 'The Foo', klass).sort_by { |i| i.stuff[2] } # Check (0..expected_out.size - 1).each do |i| assert_equal expected_out[i].stuff[0], actual_out[i].stuff[0].string, 'content must match' assert_equal expected_out[i].stuff[2], actual_out[i].stuff[2], 'identifier must match' ['num', :content_filename, :meta_filename, :extension, :mtime].each do |key| assert_equal expected_out[i].stuff[1][key], actual_out[i].stuff[1][key], "attribute key #{key} must match" end end end def test_load_objects_correct_identifier_with_separate_yaml_file data_source = new_data_source({ identifier_type: 'full' }) FileUtils.mkdir_p('foo') File.write('foo/donkey.jpeg', 'data') File.write('foo/donkey.yaml', "---\nalt: Donkey\n") objects = data_source.send(:load_objects, 'foo', 'The Foo', Nanoc::Int::Item) assert_equal 1, objects.size assert_equal '/donkey.jpeg', objects.first.identifier.to_s end def test_filename_for data_source = new_data_source assert_equal '/foo.bar', data_source.send(:filename_for, '/foo', 'bar') assert_equal '/foo.bar.baz', data_source.send(:filename_for, '/foo', 'bar.baz') assert_equal '/foo', data_source.send(:filename_for, '/foo', '') assert_equal nil, data_source.send(:filename_for, '/foo', nil) end def test_compile_huge_site if_implemented do # Create data source data_source = new_data_source # Create a lot of items count = Process.getrlimit(Process::RLIMIT_NOFILE)[0] + 5 count.times do |i| FileUtils.mkdir_p("content/#{i}") File.open("content/#{i}/#{i}.html", 'w') { |io| io << "This is item #{i}." } File.open("content/#{i}/#{i}.yaml", 'w') { |io| io << "title: Item #{i}" } end # Read all items data_source.items end end def test_compile_iso_8859_1_site # Check encoding unless ''.respond_to?(:encode) skip 'Test only works on 1.9.x' return end # Create data source data_source = new_data_source # Create item FileUtils.mkdir_p('content') File.open('content/foo.md', 'w') { |io| io << 'Hëllö' } # Parse begin original_default_external_encoding = Encoding.default_external Encoding.default_external = 'ISO-8859-1' items = data_source.items assert_equal 1, items.size assert_equal Encoding.find('UTF-8'), items[0].content.string.encoding ensure Encoding.default_external = original_default_external_encoding end end def test_compile_iso_8859_1_site_with_explicit_encoding # Check encoding unless ''.respond_to?(:encode) skip 'Test only works on 1.9.x' return end # Create data source data_source = new_data_source({}) data_source.config[:encoding] = 'ISO-8859-1' # Create item begin original_default_external_encoding = Encoding.default_external Encoding.default_external = 'ISO-8859-1' FileUtils.mkdir_p('content') File.open('content/foo.md', 'w') { |io| io << 'Hëllö' } ensure Encoding.default_external = original_default_external_encoding end # Parse items = data_source.items assert_equal 1, items.size assert_equal Encoding.find('UTF-8'), items[0].content.string.encoding end end nanoc-4.1.4/test/data_sources/test_filesystem.rb0000644000004100000410000003500612665031555022065 0ustar www-datawww-dataclass Nanoc::DataSources::FilesystemTest < Nanoc::TestCase class SampleFilesystemDataSource < Nanoc::DataSource include Nanoc::DataSources::Filesystem end def test_items # Create data source data_source = SampleFilesystemDataSource.new(nil, nil, nil, nil) # Check data_source.expects(:load_objects).with('content', 'item', Nanoc::Int::Item) data_source.items end def test_layouts # Create data source data_source = SampleFilesystemDataSource.new(nil, nil, nil, nil) # Check data_source.expects(:load_objects).with('layouts', 'layout', Nanoc::Int::Layout) data_source.layouts end def test_all_split_files_in_allowing_periods_in_identifiers # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, { allow_periods_in_identifiers: true }) # Write sample files FileUtils.mkdir_p('foo') %w( foo.html foo.yaml bar.entry.html foo/qux.yaml ).each do |filename| File.open(filename, 'w') { |io| io.write('test') } end # Write stray files %w( foo.html~ foo.yaml.orig bar.entry.html.bak ).each do |filename| File.open(filename, 'w') { |io| io.write('test') } end # Get all files output_expected = { './foo' => ['yaml', ['html']], './bar.entry' => [nil, ['html']], './foo/qux' => ['yaml', [nil]], } output_actual = data_source.send :all_split_files_in, '.' # Check assert_equal output_expected, output_actual end def test_all_split_files_in_disallowing_periods_in_identifiers # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Write sample files FileUtils.mkdir_p('foo') %w( foo.html foo.yaml bar.html.erb foo/qux.yaml ).each do |filename| File.open(filename, 'w') { |io| io.write('test') } end # Write stray files %w( foo.html~ foo.yaml.orig bar.entry.html.bak ).each do |filename| File.open(filename, 'w') { |io| io.write('test') } end # Get all files output_expected = { './foo' => ['yaml', ['html']], './bar' => [nil, ['html.erb']], './foo/qux' => ['yaml', [nil]], } output_actual = data_source.send :all_split_files_in, '.' # Check assert_equal output_expected, output_actual end def test_all_split_files_in_with_multiple_dirs # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Write sample files %w( aaa/foo.html bbb/foo.html ccc/foo.html ).each do |filename| FileUtils.mkdir_p(File.dirname(filename)) File.open(filename, 'w') { |io| io.write('test') } end # Check expected = { './aaa/foo' => [nil, ['html']], './bbb/foo' => [nil, ['html']], './ccc/foo' => [nil, ['html']], } assert_equal expected, data_source.send(:all_split_files_in, '.') end def test_all_split_files_in_with_same_extensions # Create data source config = { identifier_type: 'full' } data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, config) # Write sample files %w( stuff/foo.html stuff/foo.md stuff/foo.yaml ).each do |filename| FileUtils.mkdir_p(File.dirname(filename)) File.open(filename, 'w') { |io| io.write('test') } end # Check - { './stuff/foo' => ['yaml', ['html', 'md']] } res = data_source.send(:all_split_files_in, '.') assert_equal ['./stuff/foo'], res.keys assert_equal 2, res.values[0].size assert_equal 'yaml', res.values[0][0] assert_equal Array, res.values[0][1].class assert_equal %w(html md), res.values[0][1].sort end def test_all_split_files_in_with_multiple_content_files # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Write sample files %w( foo.html foo.xhtml foo.txt foo.yaml bar.html qux.yaml ).each do |filename| File.open(filename, 'w') { |io| io.write('test') } end # Check assert_raises RuntimeError do data_source.send(:all_split_files_in, '.') end end def test_basename_of_allowing_periods_in_identifiers # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, { allow_periods_in_identifiers: true }) # Get input and expected output expected = { '/' => '/', '/foo' => '/foo', '/foo.html' => '/foo', '/foo.xyz.html' => '/foo.xyz', '/foo/' => '/foo/', '/foo.xyz/' => '/foo.xyz/', '/foo/bar' => '/foo/bar', '/foo/bar.html' => '/foo/bar', '/foo/bar.xyz.html' => '/foo/bar.xyz', '/foo/bar/' => '/foo/bar/', '/foo/bar.xyz/' => '/foo/bar.xyz/', '/foo.xyz/bar.xyz/' => '/foo.xyz/bar.xyz/', } # Check expected.each_pair do |input, expected_output| actual_output = data_source.send(:basename_of, input) assert_equal( expected_output, actual_output, "basename_of(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_basename_of_disallowing_periods_in_identifiers # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Get input and expected output expected = { '/' => '/', '/foo' => '/foo', '/foo.html' => '/foo', '/foo.xyz.html' => '/foo', '/foo/' => '/foo/', '/foo.xyz/' => '/foo.xyz/', '/foo/bar' => '/foo/bar', '/foo/bar.html' => '/foo/bar', '/foo/bar.xyz.html' => '/foo/bar', '/foo/bar/' => '/foo/bar/', '/foo/bar.xyz/' => '/foo/bar.xyz/', '/foo.xyz/bar.xyz/' => '/foo.xyz/bar.xyz/', } # Check expected.each_pair do |input, expected_output| actual_output = data_source.send(:basename_of, input) assert_equal( expected_output, actual_output, "basename_of(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_ext_of_allowing_periods_in_identifiers # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, { allow_periods_in_identifiers: true }) # Get input and expected output expected = { '/' => '', '/foo' => '', '/foo.html' => '.html', '/foo.xyz.html' => '.html', '/foo/' => '', '/foo.xyz/' => '', '/foo/bar' => '', '/foo/bar.html' => '.html', '/foo/bar.xyz.html' => '.html', '/foo/bar/' => '', '/foo/bar.xyz/' => '', '/foo.xyz/bar.xyz/' => '', } # Check expected.each_pair do |input, expected_output| actual_output = data_source.send(:ext_of, input) assert_equal( expected_output, actual_output, "basename_of(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_ext_of_disallowing_periods_in_identifiers # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Get input and expected output expected = { '/' => '', '/foo' => '', '/foo.html' => '.html', '/foo.xyz.html' => '.xyz.html', '/foo/' => '', '/foo.xyz/' => '', '/foo/bar' => '', '/foo/bar.html' => '.html', '/foo/bar.xyz.html' => '.xyz.html', '/foo/bar/' => '', '/foo/bar.xyz/' => '', '/foo.xyz/bar.xyz/' => '', } # Check expected.each_pair do |input, expected_output| actual_output = data_source.send(:ext_of, input) assert_equal( expected_output, actual_output, "basename_of(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}" ) end end def test_parse_embedded_meta_only_1 # Create a file File.open('test.html', 'w') do |io| io.write "-----\r\n" io.write "foo: bar\n" io.write "-----\n" end # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({ 'foo' => 'bar' }, result[0]) assert_equal('', result[1]) end def test_parse_embedded_meta_only_2 # Create a file File.open('test.html', 'w') do |io| io.write "-----\n" io.write "foo: bar\r\n" io.write "-----\r" end # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({ 'foo' => 'bar' }, result[0]) assert_equal('', result[1]) end def test_parse_embedded_meta_only_3 # Create a file File.open('test.html', 'w') do |io| io.write "-----\r\n" io.write "foo: bar\n" io.write '-----' end # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({ 'foo' => 'bar' }, result[0]) assert_equal('', result[1]) end def test_parse_embedded_invalid_2 # Create a file File.open('test.html', 'w') do |io| io.write "-----\n" io.write "blah blah\n" end # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it assert_raises(RuntimeError) do data_source.instance_eval { parse('test.html', nil, 'foobar') } end end def test_parse_embedded_separators_but_not_metadata # Create a file File.open('test.html', 'w') do |io| io.write "blah blah\n" io.write "-----\n" io.write "blah blah\n" end # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal(File.read('test.html'), result[1]) assert_equal({}, result[0]) end def test_parse_embedded_full_meta # Create a file File.open('test.html', 'w') do |io| io.write "-----\r\n" io.write "foo: bar\n" io.write "-----\n" io.write " \t\n blah blah\n" end # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({ 'foo' => 'bar' }, result[0]) assert_equal(" \t\n blah blah\n", result[1]) end def test_parse_embedded_with_extra_spaces # Create a file File.open('test.html', 'w') do |io| io.write "----- \n" io.write "foo: bar\n" io.write "-----\t\t\t\t\t\n" io.write " blah blah\n" end # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({ 'foo' => 'bar' }, result[0]) assert_equal(" blah blah\n", result[1]) end def test_parse_embedded_empty_meta # Create a file File.open('test.html', 'w') do |io| io.write "-----\n" io.write "-----\n" io.write "\nblah blah\n" io.write '-----' end # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({}, result[0]) assert_equal("\nblah blah\n-----", result[1]) end def test_parse_utf8_bom File.open('test.html', 'w') do |io| io.write [0xEF, 0xBB, 0xBF].map(&:chr).join io.write "-----\n" io.write "utf8bomawareness: high\n" io.write "-----\n" io.write "content goes here\n" end data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, encoding: 'utf-8') result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({ 'utf8bomawareness' => 'high' }, result[0]) assert_equal("content goes here\n", result[1]) end def test_parse_embedded_no_meta content = "blah\n" \ "blah blah blah\n" \ "blah blah\n" # Create a file File.open('test.html', 'w') { |io| io.write(content) } # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({}, result[0]) assert_equal(content, result[1]) end def test_parse_embedded_diff content = \ "--- a/foo\n" \ "+++ b/foo\n" \ "blah blah\n" # Create a file File.open('test.html', 'w') { |io| io.write(content) } # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', nil, 'foobar') } assert_equal({}, result[0]) assert_equal(content, result[1]) end def test_parse_external # Create a file File.open('test.html', 'w') { |io| io.write('blah blah') } File.open('test.yaml', 'w') { |io| io.write('foo: bar') } # Create data source data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) # Parse it result = data_source.instance_eval { parse('test.html', 'test.yaml', 'foobar') } assert_equal({ 'foo' => 'bar' }, result[0]) assert_equal('blah blah', result[1]) end def test_parse_internal_bad_metadata content = \ "---\n" \ "Hello world!\n" \ "---\n" \ "blah blah\n" File.open('test.html', 'w') { |io| io.write(content) } data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) assert_raises(Nanoc::DataSources::Filesystem::InvalidMetadataError) do data_source.instance_eval { parse('test.html', nil, 'foobar') } end end def test_parse_external_bad_metadata File.open('test.html', 'w') { |io| io.write('blah blah') } File.open('test.yaml', 'w') { |io| io.write('Hello world!') } data_source = Nanoc::DataSources::FilesystemUnified.new(nil, nil, nil, nil) assert_raises(Nanoc::DataSources::Filesystem::InvalidMetadataError) do data_source.instance_eval { parse('test.html', 'test.yaml', 'foobar') } end end end nanoc-4.1.4/tasks/0000755000004100000410000000000012665031555014003 5ustar www-datawww-datananoc-4.1.4/tasks/rubocop.rake0000644000004100000410000000032612665031555016321 0ustar www-datawww-datarequire 'rubocop/rake_task' RuboCop::RakeTask.new(:rubocop) do |task| task.options = %w( --display-cop-names --format simple ) task.patterns = ['bin/nanoc', 'lib/**/*.rb', 'spec/**/*.rb', 'test/**/*.rb'] end nanoc-4.1.4/tasks/test.rake0000644000004100000410000000121712665031555015627 0ustar www-datawww-datarequire 'rspec/core/rake_task' require 'rake/testtask' require 'coveralls/rake/task' Coveralls::RakeTask.new SUBDIRS = %w( * base cli data_sources extra filters helpers ).freeze namespace :test do SUBDIRS.each do |dir| Rake::TestTask.new(dir == '*' ? 'all' : dir) do |t| t.test_files = Dir["test/#{dir}/**/*_spec.rb"] + Dir["test/#{dir}/**/test_*.rb"] t.libs = ['./lib', '.'] t.ruby_opts = ['-r./test/helper'] end end end RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = '-r ./spec/spec_helper.rb --color' t.verbose = false end desc 'Run all tests and specs' task test: [:spec, :'test:all', :'coveralls:push'] nanoc-4.1.4/tasks/doc.rake0000644000004100000410000000100012665031555015403 0ustar www-datawww-datarequire 'yard' YARD::Rake::YardocTask.new(:doc) do |yard| yard.files = Dir['lib/**/*.rb'] yard.options = [ '--markup', 'markdown', '--markup-provider', 'kramdown', '--charset', 'utf-8', '--readme', 'README.md', '--files', 'NEWS.md,LICENSE', '--output-dir', 'doc/yardoc', '--template-path', 'doc/yardoc_templates', '--load', 'doc/yardoc_handlers/identifier.rb', '--query', '@api.text != "private"' ] end nanoc-4.1.4/CONTRIBUTING.md0000644000004100000410000000471212665031555015113 0ustar www-datawww-dataContributing ============ Reporting bugs -------------- If you find a bug in Nanoc, you should report it! Some information that you should include in your bug report is the Nanoc version (`nanoc --version`) and, if relevant, the crash log (`crash.log`). For details, check the [*bug reporting* section of the development guide](http://nanoc.ws/development/#reporting-bugs). Contributing code ----------------- Pull requests are appreciated! When submitting a PR, be sure to submit it onto the right branch: * For bug fixes, use the release branch, e.g. `release-3.6.x` * For features, use the `master` branch When submitting a PR, make sure that your changes have covering tests, that the documentation remains up-to-date and that you retain backwards compatibility. For details, check the [*contributing code* section of the development guide](http://nanoc.ws/development/#contributing-code). Contributor Code of Conduct --------------------------- As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) nanoc-4.1.4/LICENSE0000644000004100000410000000207012665031555013662 0ustar www-datawww-dataCopyright (c) 2007-2016 Denis Defreyne and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nanoc-4.1.4/ChangeLog0000644000004100000410000000024512665031555014431 0ustar www-datawww-dataFor a list of all changes, please see the changelog on the project repository instead (https://github.com/nanoc/nanoc). For release notes, please see the NEWS file. nanoc-4.1.4/README.md0000644000004100000410000000432412665031555014140 0ustar www-datawww-data[![Gem version](http://img.shields.io/gem/v/nanoc.svg)](http://rubygems.org/gems/nanoc) [![Build status](http://img.shields.io/travis/nanoc/nanoc.svg)](https://travis-ci.org/nanoc/nanoc) [![Code Climate](http://img.shields.io/codeclimate/github/nanoc/nanoc.svg)](https://codeclimate.com/github/nanoc/nanoc) [![Code Coverage](http://img.shields.io/coveralls/nanoc/nanoc.svg)](https://coveralls.io/r/nanoc/nanoc) ![Nanoc logo](https://avatars1.githubusercontent.com/u/3260163?s=140) # Nanoc Nanoc is a flexible static-site generator written in Ruby. See the [Nanoc web site](http://nanoc.ws) for more information. **Please take a moment and [donate](http://pledgie.com/campaigns/9282) to Nanoc. A lot of time has gone into developing Nanoc, and I would like to keep it going. Your support will ensure that Nanoc will continue to improve.** ## Contributing Contributions are greatly appreciated! Consult the [Development guidelines](http://nanoc.ws/development/) for information on how you can contribute. ### Contributors Many thanks to everyone who has contributed to Nanoc in one way or another: Ale Muñoz, Alexander Mankuta, Arnau Siches, Ben Armston, Bil Bas, Brian Candler, Bruno Dufour, Chris Eppstein, Christian Plessl, Colin Barrett, Colin Seymour, Croath Liu, Damien Pollet, Dan Callahan, Daniel Hofstetter, Daniel Mendler, Daniel Wollschlaeger, David Alexander, David Everitt, Dennis Sutch, Devon Luke Buchanan, Dmitry Bilunov, Eric Sunshine, Erik Hollensbe, Fabian Buch, Felix Hanley, Garen Torikian, Go Maeda, Gregory Pakosz, Grégory Karékinian, Guilherme Garnier, Jack Chu, Jake Benilov, Jasper Van der Jeugt, Jeff Forcier, Jim Mendenhall, John Nishinaga, Justin Clift, Justin Hileman, Kevin Lynagh, Louis T., Mathias Bynens, Matt Keveney, Matthew Frazier, Matthias Beyer, Matthias Reitinger, Matthias Vallentin, Michal Cichra, Nelson Chen, Nicky Peeters, Nikhil Marathe, Oliver Byford, Peter Aronoff, Raphael von der Grün, Rémi Barraquand, Remko Tronçon, Riley Goodside, Ruben Verborgh, Scott Vokes, Šime Ramov, Simon South, Spencer Whitt, Stanley Rost, Starr Horne, Stefan Bühler, Stuart Montgomery, Takashi Uchibe, Toon Willems, Tuomas Kareinen, Ursula Kallio, Vincent Driessen, Xavier Shay, Yannick Ihmels, Zaiste de Grengolada nanoc-4.1.4/Guardfile0000644000004100000410000000010412665031555014476 0ustar www-datawww-dataguard 'rake', task: 'default' do watch(%r{^(lib|test|spec)/}) end