nanoc 3.2.4 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/ChangeLog +3 -0
- data/Gemfile +32 -0
- data/LICENSE +19 -0
- data/NEWS.md +470 -0
- data/README.md +114 -0
- data/Rakefile +14 -0
- data/bin/nanoc +7 -27
- data/bin/nanoc3 +3 -0
- data/doc/yardoc_templates/default/layout/html/footer.erb +10 -0
- data/lib/nanoc.rb +41 -0
- data/lib/nanoc/base.rb +49 -0
- data/lib/nanoc/base/compilation/checksum_store.rb +57 -0
- data/lib/nanoc/base/compilation/compiled_content_cache.rb +62 -0
- data/lib/nanoc/base/compilation/compiler.rb +458 -0
- data/lib/nanoc/base/compilation/compiler_dsl.rb +214 -0
- data/lib/nanoc/base/compilation/dependency_tracker.rb +200 -0
- data/lib/nanoc/base/compilation/filter.rb +165 -0
- data/lib/nanoc/base/compilation/item_rep_proxy.rb +103 -0
- data/lib/nanoc/base/compilation/item_rep_recorder_proxy.rb +102 -0
- data/lib/nanoc/base/compilation/outdatedness_checker.rb +223 -0
- data/lib/nanoc/base/compilation/outdatedness_reasons.rb +46 -0
- data/lib/nanoc/base/compilation/rule.rb +73 -0
- data/lib/nanoc/base/compilation/rule_context.rb +84 -0
- data/lib/nanoc/base/compilation/rule_memory_calculator.rb +40 -0
- data/lib/nanoc/base/compilation/rule_memory_store.rb +53 -0
- data/lib/nanoc/base/compilation/rules_collection.rb +243 -0
- data/lib/nanoc/base/context.rb +47 -0
- data/lib/nanoc/base/core_ext.rb +6 -0
- data/lib/nanoc/base/core_ext/array.rb +62 -0
- data/lib/nanoc/base/core_ext/hash.rb +63 -0
- data/lib/nanoc/base/core_ext/pathname.rb +26 -0
- data/lib/nanoc/base/core_ext/string.rb +46 -0
- data/lib/nanoc/base/directed_graph.rb +275 -0
- data/lib/nanoc/base/errors.rb +211 -0
- data/lib/nanoc/base/memoization.rb +67 -0
- data/lib/nanoc/base/notification_center.rb +84 -0
- data/lib/nanoc/base/ordered_hash.rb +200 -0
- data/lib/nanoc/base/plugin_registry.rb +181 -0
- data/lib/nanoc/base/result_data/item_rep.rb +492 -0
- data/lib/nanoc/base/source_data/code_snippet.rb +58 -0
- data/lib/nanoc/base/source_data/configuration.rb +24 -0
- data/lib/nanoc/base/source_data/data_source.rb +234 -0
- data/lib/nanoc/base/source_data/item.rb +301 -0
- data/lib/nanoc/base/source_data/layout.rb +130 -0
- data/lib/nanoc/base/source_data/site.rb +361 -0
- data/lib/nanoc/base/store.rb +135 -0
- data/lib/nanoc/cli.rb +137 -0
- data/lib/nanoc/cli/command_runner.rb +104 -0
- data/lib/nanoc/cli/commands/autocompile.rb +58 -0
- data/lib/nanoc/cli/commands/compile.rb +297 -0
- data/lib/nanoc/cli/commands/create_item.rb +60 -0
- data/lib/nanoc/cli/commands/create_layout.rb +73 -0
- data/lib/nanoc/cli/commands/create_site.rb +411 -0
- data/lib/nanoc/cli/commands/debug.rb +117 -0
- data/lib/nanoc/cli/commands/deploy.rb +79 -0
- data/lib/nanoc/cli/commands/info.rb +98 -0
- data/lib/nanoc/cli/commands/nanoc.rb +38 -0
- data/lib/nanoc/cli/commands/prune.rb +50 -0
- data/lib/nanoc/cli/commands/update.rb +70 -0
- data/lib/nanoc/cli/commands/view.rb +82 -0
- data/lib/nanoc/cli/commands/watch.rb +124 -0
- data/lib/nanoc/cli/error_handler.rb +199 -0
- data/lib/nanoc/cli/logger.rb +92 -0
- data/lib/nanoc/data_sources.rb +29 -0
- data/lib/nanoc/data_sources/deprecated/delicious.rb +42 -0
- data/lib/nanoc/data_sources/deprecated/last_fm.rb +87 -0
- data/lib/nanoc/data_sources/deprecated/twitter.rb +38 -0
- data/lib/nanoc/data_sources/filesystem.rb +299 -0
- data/lib/nanoc/data_sources/filesystem_unified.rb +121 -0
- data/lib/nanoc/data_sources/filesystem_verbose.rb +91 -0
- data/lib/nanoc/extra.rb +24 -0
- data/lib/nanoc/extra/auto_compiler.rb +103 -0
- data/lib/nanoc/extra/chick.rb +125 -0
- data/lib/nanoc/extra/core_ext.rb +6 -0
- data/lib/nanoc/extra/core_ext/enumerable.rb +33 -0
- data/lib/nanoc/extra/core_ext/pathname.rb +30 -0
- data/lib/nanoc/extra/core_ext/time.rb +19 -0
- data/lib/nanoc/extra/deployer.rb +47 -0
- data/lib/nanoc/extra/deployers.rb +15 -0
- data/lib/nanoc/extra/deployers/fog.rb +98 -0
- data/lib/nanoc/extra/deployers/rsync.rb +70 -0
- data/lib/nanoc/extra/file_proxy.rb +40 -0
- data/lib/nanoc/extra/pruner.rb +86 -0
- data/lib/nanoc/extra/validators.rb +12 -0
- data/lib/nanoc/extra/validators/links.rb +268 -0
- data/lib/nanoc/extra/validators/w3c.rb +95 -0
- data/lib/nanoc/extra/vcs.rb +66 -0
- data/lib/nanoc/extra/vcses.rb +17 -0
- data/lib/nanoc/extra/vcses/bazaar.rb +25 -0
- data/lib/nanoc/extra/vcses/dummy.rb +24 -0
- data/lib/nanoc/extra/vcses/git.rb +25 -0
- data/lib/nanoc/extra/vcses/mercurial.rb +25 -0
- data/lib/nanoc/extra/vcses/subversion.rb +25 -0
- data/lib/nanoc/filters.rb +59 -0
- data/lib/nanoc/filters/asciidoc.rb +38 -0
- data/lib/nanoc/filters/bluecloth.rb +19 -0
- data/lib/nanoc/filters/coderay.rb +21 -0
- data/lib/nanoc/filters/coffeescript.rb +20 -0
- data/lib/nanoc/filters/colorize_syntax.rb +298 -0
- data/lib/nanoc/filters/erb.rb +38 -0
- data/lib/nanoc/filters/erubis.rb +34 -0
- data/lib/nanoc/filters/haml.rb +27 -0
- data/lib/nanoc/filters/kramdown.rb +20 -0
- data/lib/nanoc/filters/less.rb +53 -0
- data/lib/nanoc/filters/markaby.rb +20 -0
- data/lib/nanoc/filters/maruku.rb +20 -0
- data/lib/nanoc/filters/mustache.rb +24 -0
- data/lib/nanoc/filters/rainpress.rb +19 -0
- data/lib/nanoc/filters/rdiscount.rb +22 -0
- data/lib/nanoc/filters/rdoc.rb +33 -0
- data/lib/nanoc/filters/redcarpet.rb +62 -0
- data/lib/nanoc/filters/redcloth.rb +47 -0
- data/lib/nanoc/filters/relativize_paths.rb +94 -0
- data/lib/nanoc/filters/rubypants.rb +20 -0
- data/lib/nanoc/filters/sass.rb +74 -0
- data/lib/nanoc/filters/slim.rb +25 -0
- data/lib/nanoc/filters/typogruby.rb +23 -0
- data/lib/nanoc/filters/uglify_js.rb +42 -0
- data/lib/nanoc/filters/xsl.rb +46 -0
- data/lib/nanoc/filters/yui_compressor.rb +23 -0
- data/lib/nanoc/helpers.rb +16 -0
- data/lib/nanoc/helpers/blogging.rb +319 -0
- data/lib/nanoc/helpers/breadcrumbs.rb +40 -0
- data/lib/nanoc/helpers/capturing.rb +138 -0
- data/lib/nanoc/helpers/filtering.rb +50 -0
- data/lib/nanoc/helpers/html_escape.rb +55 -0
- data/lib/nanoc/helpers/link_to.rb +151 -0
- data/lib/nanoc/helpers/rendering.rb +140 -0
- data/lib/nanoc/helpers/tagging.rb +71 -0
- data/lib/nanoc/helpers/text.rb +44 -0
- data/lib/nanoc/helpers/xml_sitemap.rb +76 -0
- data/lib/nanoc/tasks.rb +10 -0
- data/lib/nanoc/tasks/clean.rake +16 -0
- data/lib/nanoc/tasks/clean.rb +29 -0
- data/lib/nanoc/tasks/deploy/rsync.rake +16 -0
- data/lib/nanoc/tasks/validate.rake +92 -0
- data/nanoc.gemspec +49 -0
- data/tasks/doc.rake +16 -0
- data/tasks/test.rake +46 -0
- data/test/base/core_ext/array_spec.rb +73 -0
- data/test/base/core_ext/hash_spec.rb +98 -0
- data/test/base/core_ext/pathname_spec.rb +27 -0
- data/test/base/core_ext/string_spec.rb +37 -0
- data/test/base/test_checksum_store.rb +35 -0
- data/test/base/test_code_snippet.rb +31 -0
- data/test/base/test_compiler.rb +403 -0
- data/test/base/test_compiler_dsl.rb +161 -0
- data/test/base/test_context.rb +31 -0
- data/test/base/test_data_source.rb +46 -0
- data/test/base/test_dependency_tracker.rb +262 -0
- data/test/base/test_directed_graph.rb +288 -0
- data/test/base/test_filter.rb +83 -0
- data/test/base/test_item.rb +179 -0
- data/test/base/test_item_rep.rb +579 -0
- data/test/base/test_layout.rb +59 -0
- data/test/base/test_memoization.rb +90 -0
- data/test/base/test_notification_center.rb +34 -0
- data/test/base/test_outdatedness_checker.rb +394 -0
- data/test/base/test_plugin.rb +30 -0
- data/test/base/test_rule.rb +19 -0
- data/test/base/test_rule_context.rb +65 -0
- data/test/base/test_site.rb +190 -0
- data/test/cli/commands/test_compile.rb +33 -0
- data/test/cli/commands/test_create_item.rb +14 -0
- data/test/cli/commands/test_create_layout.rb +28 -0
- data/test/cli/commands/test_create_site.rb +24 -0
- data/test/cli/commands/test_deploy.rb +74 -0
- data/test/cli/commands/test_help.rb +12 -0
- data/test/cli/commands/test_info.rb +11 -0
- data/test/cli/commands/test_prune.rb +98 -0
- data/test/cli/commands/test_update.rb +10 -0
- data/test/cli/test_cli.rb +102 -0
- data/test/cli/test_error_handler.rb +29 -0
- data/test/cli/test_logger.rb +10 -0
- data/test/data_sources/test_filesystem.rb +433 -0
- data/test/data_sources/test_filesystem_unified.rb +536 -0
- data/test/data_sources/test_filesystem_verbose.rb +357 -0
- data/test/extra/core_ext/test_enumerable.rb +30 -0
- data/test/extra/core_ext/test_pathname.rb +17 -0
- data/test/extra/core_ext/test_time.rb +15 -0
- data/test/extra/deployers/test_fog.rb +67 -0
- data/test/extra/deployers/test_rsync.rb +100 -0
- data/test/extra/test_auto_compiler.rb +417 -0
- data/test/extra/test_file_proxy.rb +19 -0
- data/test/extra/test_vcs.rb +22 -0
- data/test/extra/validators/test_links.rb +62 -0
- data/test/extra/validators/test_w3c.rb +47 -0
- data/test/filters/test_asciidoc.rb +22 -0
- data/test/filters/test_bluecloth.rb +18 -0
- data/test/filters/test_coderay.rb +44 -0
- data/test/filters/test_coffeescript.rb +18 -0
- data/test/filters/test_colorize_syntax.rb +379 -0
- data/test/filters/test_erb.rb +105 -0
- data/test/filters/test_erubis.rb +78 -0
- data/test/filters/test_haml.rb +96 -0
- data/test/filters/test_kramdown.rb +18 -0
- data/test/filters/test_less.rb +113 -0
- data/test/filters/test_markaby.rb +24 -0
- data/test/filters/test_maruku.rb +18 -0
- data/test/filters/test_mustache.rb +25 -0
- data/test/filters/test_rainpress.rb +29 -0
- data/test/filters/test_rdiscount.rb +31 -0
- data/test/filters/test_rdoc.rb +18 -0
- data/test/filters/test_redcarpet.rb +73 -0
- data/test/filters/test_redcloth.rb +33 -0
- data/test/filters/test_relativize_paths.rb +533 -0
- data/test/filters/test_rubypants.rb +18 -0
- data/test/filters/test_sass.rb +229 -0
- data/test/filters/test_slim.rb +35 -0
- data/test/filters/test_typogruby.rb +21 -0
- data/test/filters/test_uglify_js.rb +30 -0
- data/test/filters/test_xsl.rb +105 -0
- data/test/filters/test_yui_compressor.rb +44 -0
- data/test/gem_loader.rb +11 -0
- data/test/helper.rb +207 -0
- data/test/helpers/test_blogging.rb +754 -0
- data/test/helpers/test_breadcrumbs.rb +81 -0
- data/test/helpers/test_capturing.rb +41 -0
- data/test/helpers/test_filtering.rb +106 -0
- data/test/helpers/test_html_escape.rb +32 -0
- data/test/helpers/test_link_to.rb +249 -0
- data/test/helpers/test_rendering.rb +89 -0
- data/test/helpers/test_tagging.rb +87 -0
- data/test/helpers/test_text.rb +24 -0
- data/test/helpers/test_xml_sitemap.rb +103 -0
- data/test/tasks/test_clean.rb +67 -0
- metadata +327 -15
- data/bin/nanoc-select +0 -86
- data/lib/nanoc-select.rb +0 -11
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc
|
|
4
|
+
|
|
5
|
+
# Provides a context and a binding for use in filters such as the ERB and
|
|
6
|
+
# Haml ones.
|
|
7
|
+
class Context
|
|
8
|
+
|
|
9
|
+
# Creates a new context based off the contents of the hash.
|
|
10
|
+
#
|
|
11
|
+
# Each pair in the hash will be converted to an instance variable and an
|
|
12
|
+
# instance method. For example, passing the hash `{ :foo => 'bar' }` will
|
|
13
|
+
# cause `@foo` to have the value `"bar"`, and the instance method `#foo`
|
|
14
|
+
# to return the same value `"bar"`.
|
|
15
|
+
#
|
|
16
|
+
# @param [Hash] hash A list of key-value pairs to make available
|
|
17
|
+
#
|
|
18
|
+
# @example Defining a context and accessing values
|
|
19
|
+
#
|
|
20
|
+
# context = Nanoc::Context.new(
|
|
21
|
+
# :name => 'Max Payne',
|
|
22
|
+
# :location => 'in a cheap motel'
|
|
23
|
+
# )
|
|
24
|
+
# context.instance_eval do
|
|
25
|
+
# "I am #{name} and I am hiding #{@location}."
|
|
26
|
+
# end
|
|
27
|
+
# # => "I am Max Payne and I am hiding in a cheap motel."
|
|
28
|
+
def initialize(hash)
|
|
29
|
+
hash.each_pair do |key, value|
|
|
30
|
+
# Build instance variable
|
|
31
|
+
instance_variable_set('@' + key.to_s, value)
|
|
32
|
+
|
|
33
|
+
# Define method
|
|
34
|
+
metaclass = (class << self ; self ; end)
|
|
35
|
+
metaclass.send(:define_method, key) { value }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns a binding for this instance.
|
|
40
|
+
#
|
|
41
|
+
# @return [Binding] A binding for this instance
|
|
42
|
+
def get_binding
|
|
43
|
+
binding
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc::ArrayExtensions
|
|
4
|
+
|
|
5
|
+
# Returns a new array where all items' keys are recursively converted to
|
|
6
|
+
# symbols by calling {Nanoc::ArrayExtensions#symbolize_keys} or
|
|
7
|
+
# {Nanoc::HashExtensions#symbolize_keys}.
|
|
8
|
+
#
|
|
9
|
+
# @return [Array] The converted array
|
|
10
|
+
def symbolize_keys
|
|
11
|
+
inject([]) do |array, element|
|
|
12
|
+
array + [ element.respond_to?(:symbolize_keys) ? element.symbolize_keys : element ]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns a new array where all items' keys are recursively converted to
|
|
17
|
+
# strings by calling {Nanoc::ArrayExtensions#stringify_keys} or
|
|
18
|
+
# {Nanoc::HashExtensions#stringify_keys}.
|
|
19
|
+
#
|
|
20
|
+
# @return [Array] The converted array
|
|
21
|
+
def stringify_keys
|
|
22
|
+
inject([]) do |array, element|
|
|
23
|
+
array + [ element.respond_to?(:stringify_keys) ? element.stringify_keys : element ]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Freezes the contents of the array, as well as all array elements. The
|
|
28
|
+
# array elements will be frozen using {#freeze_recursively} if they respond
|
|
29
|
+
# to that message, or #freeze if they do not.
|
|
30
|
+
#
|
|
31
|
+
# @see Hash#freeze_recursively
|
|
32
|
+
#
|
|
33
|
+
# @return [void]
|
|
34
|
+
#
|
|
35
|
+
# @since 3.2.0
|
|
36
|
+
def freeze_recursively
|
|
37
|
+
return if self.frozen?
|
|
38
|
+
freeze
|
|
39
|
+
each do |value|
|
|
40
|
+
if value.respond_to?(:freeze_recursively)
|
|
41
|
+
value.freeze_recursively
|
|
42
|
+
else
|
|
43
|
+
value.freeze
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Calculates the checksum for this array. Any change to this array will
|
|
49
|
+
# result in a different checksum.
|
|
50
|
+
#
|
|
51
|
+
# @return [String] The checksum for this array
|
|
52
|
+
#
|
|
53
|
+
# @api private
|
|
54
|
+
def checksum
|
|
55
|
+
Marshal.dump(self).checksum
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class Array
|
|
61
|
+
include Nanoc::ArrayExtensions
|
|
62
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc::HashExtensions
|
|
4
|
+
|
|
5
|
+
# Returns a new hash where all keys are recursively converted to symbols by
|
|
6
|
+
# calling {Nanoc::ArrayExtensions#symbolize_keys} or
|
|
7
|
+
# {Nanoc::HashExtensions#symbolize_keys}.
|
|
8
|
+
#
|
|
9
|
+
# @return [Hash] The converted hash
|
|
10
|
+
def symbolize_keys
|
|
11
|
+
inject({}) do |hash, (key, value)|
|
|
12
|
+
hash.merge(key.to_sym => value.respond_to?(:symbolize_keys) ? value.symbolize_keys : value)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns a new hash where all keys are recursively converted to strings by
|
|
17
|
+
# calling {Nanoc::ArrayExtensions#stringify_keys} or
|
|
18
|
+
# {Nanoc::HashExtensions#stringify_keys}.
|
|
19
|
+
#
|
|
20
|
+
# @return [Hash] The converted hash
|
|
21
|
+
def stringify_keys
|
|
22
|
+
inject({}) do |hash, (key, value)|
|
|
23
|
+
hash.merge(key.to_s => value.respond_to?(:stringify_keys) ? value.stringify_keys : value)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Freezes the contents of the hash, as well as all hash values. The hash
|
|
28
|
+
# values will be frozen using {#freeze_recursively} if they respond to
|
|
29
|
+
# that message, or #freeze if they do not.
|
|
30
|
+
#
|
|
31
|
+
# @see Array#freeze_recursively
|
|
32
|
+
#
|
|
33
|
+
# @return [void]
|
|
34
|
+
#
|
|
35
|
+
# @since 3.2.0
|
|
36
|
+
def freeze_recursively
|
|
37
|
+
return if self.frozen?
|
|
38
|
+
freeze
|
|
39
|
+
each_pair do |key, value|
|
|
40
|
+
if value.respond_to?(:freeze_recursively)
|
|
41
|
+
value.freeze_recursively
|
|
42
|
+
else
|
|
43
|
+
value.freeze
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Calculates the checksum for this hash. Any change to this hash will result
|
|
49
|
+
# in a different checksum.
|
|
50
|
+
#
|
|
51
|
+
# @return [String] The checksum for this hash
|
|
52
|
+
#
|
|
53
|
+
# @api private
|
|
54
|
+
def checksum
|
|
55
|
+
array = self.to_a.sort_by { |kv| kv[0].to_s }
|
|
56
|
+
array.checksum
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class Hash
|
|
62
|
+
include Nanoc::HashExtensions
|
|
63
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc::PathnameExtensions
|
|
4
|
+
|
|
5
|
+
# Calculates the checksum for the file referenced to by this pathname. Any
|
|
6
|
+
# change to the file contents will result in a different checksum.
|
|
7
|
+
#
|
|
8
|
+
# @return [String] The checksum for this file
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
def checksum
|
|
12
|
+
digest = Digest::SHA1.new
|
|
13
|
+
File.open(self.to_s, 'r') do |io|
|
|
14
|
+
until io.eof
|
|
15
|
+
data = io.read(2**10)
|
|
16
|
+
digest.update(data)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
digest.hexdigest
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Pathname
|
|
25
|
+
include Nanoc::PathnameExtensions
|
|
26
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc::StringExtensions
|
|
4
|
+
|
|
5
|
+
# Transforms string into an actual identifier
|
|
6
|
+
#
|
|
7
|
+
# @return [String] The identifier generated from the receiver
|
|
8
|
+
def cleaned_identifier
|
|
9
|
+
"/#{self}/".gsub(/^\/+|\/+$/, '/')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Replaces Unicode characters with their ASCII decompositions if the
|
|
13
|
+
# environment does not support Unicode.
|
|
14
|
+
#
|
|
15
|
+
# This method is not suited for general usage. If you need similar
|
|
16
|
+
# functionality, consider using the Iconv library instead.
|
|
17
|
+
#
|
|
18
|
+
# @return [String] The decomposed string
|
|
19
|
+
def make_compatible_with_env
|
|
20
|
+
# Check whether environment supports Unicode
|
|
21
|
+
# FIXME this is ugly, and there most likely are better ways to do this
|
|
22
|
+
is_unicode_supported = %w( LC_ALL LC_CTYPE LANG ).any? { |e| ENV[e] =~ /UTF/ }
|
|
23
|
+
return self if is_unicode_supported
|
|
24
|
+
|
|
25
|
+
# Decompose if necessary
|
|
26
|
+
# FIXME this decomposition is not generally usable
|
|
27
|
+
self.gsub(/“|”/, '"').gsub(/‘|’/, '\'').gsub('…', '...').gsub('©', '(c)')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Calculates the checksum for this string. Any change to this string will
|
|
31
|
+
# result in a different checksum.
|
|
32
|
+
#
|
|
33
|
+
# @return [String] The checksum for this string
|
|
34
|
+
#
|
|
35
|
+
# @api private
|
|
36
|
+
def checksum
|
|
37
|
+
digest = Digest::SHA1.new
|
|
38
|
+
digest.update(self)
|
|
39
|
+
digest.hexdigest
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class String
|
|
45
|
+
include Nanoc::StringExtensions
|
|
46
|
+
end
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc
|
|
4
|
+
|
|
5
|
+
# Represents a directed graph. It is used by the dependency tracker for
|
|
6
|
+
# storing and querying dependencies between items.
|
|
7
|
+
#
|
|
8
|
+
# @example Creating and using a directed graph
|
|
9
|
+
#
|
|
10
|
+
# # Create a graph with three vertices
|
|
11
|
+
# graph = Nanoc::DirectedGraph.new(%w( a b c d ))
|
|
12
|
+
#
|
|
13
|
+
# # Add edges
|
|
14
|
+
# graph.add_edge('a', 'b')
|
|
15
|
+
# graph.add_edge('b', 'c')
|
|
16
|
+
# graph.add_edge('c', 'd')
|
|
17
|
+
#
|
|
18
|
+
# # Get (direct) predecessors
|
|
19
|
+
# graph.direct_predecessors_of('d').sort
|
|
20
|
+
# # => %w( c )
|
|
21
|
+
# graph.predecessors_of('d').sort
|
|
22
|
+
# # => %w( a b c )
|
|
23
|
+
#
|
|
24
|
+
# # Modify edges
|
|
25
|
+
# graph.delete_edge('a', 'b')
|
|
26
|
+
#
|
|
27
|
+
# # Get (direct) predecessors again
|
|
28
|
+
# graph.direct_predecessors_of('d').sort
|
|
29
|
+
# # => %w( c )
|
|
30
|
+
# graph.predecessors_of('d').sort
|
|
31
|
+
# # => %w( b c )
|
|
32
|
+
class DirectedGraph
|
|
33
|
+
|
|
34
|
+
# The set of vertices in this graph.
|
|
35
|
+
#
|
|
36
|
+
# @return [Set]
|
|
37
|
+
attr_reader :vertices
|
|
38
|
+
|
|
39
|
+
# @group Creating a graph
|
|
40
|
+
|
|
41
|
+
# Creates a new directed graph with the given vertices.
|
|
42
|
+
def initialize(vertices)
|
|
43
|
+
@vertices = Set.new(vertices)
|
|
44
|
+
|
|
45
|
+
@from_graph = {}
|
|
46
|
+
@to_graph = {}
|
|
47
|
+
|
|
48
|
+
@vertice_indexes = {}
|
|
49
|
+
@vertices.each_with_index do |v, i|
|
|
50
|
+
@vertice_indexes[v] = i
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
@roots = Set.new(@vertices)
|
|
54
|
+
|
|
55
|
+
invalidate_caches
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @group Modifying the graph
|
|
59
|
+
|
|
60
|
+
# Adds an edge from the first vertex to the second vertex.
|
|
61
|
+
#
|
|
62
|
+
# @param from Vertex where the edge should start
|
|
63
|
+
#
|
|
64
|
+
# @param to Vertex where the edge should end
|
|
65
|
+
#
|
|
66
|
+
# @return [void]
|
|
67
|
+
def add_edge(from, to)
|
|
68
|
+
add_vertex(from)
|
|
69
|
+
add_vertex(to)
|
|
70
|
+
|
|
71
|
+
@from_graph[from] ||= Set.new
|
|
72
|
+
@from_graph[from] << to
|
|
73
|
+
|
|
74
|
+
@to_graph[to] ||= Set.new
|
|
75
|
+
@to_graph[to] << from
|
|
76
|
+
|
|
77
|
+
@roots.delete(to)
|
|
78
|
+
|
|
79
|
+
invalidate_caches
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Removes the edge from the first vertex to the second vertex. If the
|
|
83
|
+
# edge does not exist, nothing is done.
|
|
84
|
+
#
|
|
85
|
+
# @param from Start vertex of the edge
|
|
86
|
+
#
|
|
87
|
+
# @param to End vertex of the edge
|
|
88
|
+
#
|
|
89
|
+
# @return [void]
|
|
90
|
+
#
|
|
91
|
+
# @since 3.2.0
|
|
92
|
+
def delete_edge(from, to)
|
|
93
|
+
@from_graph[from] ||= Set.new
|
|
94
|
+
@from_graph[from].delete(to)
|
|
95
|
+
|
|
96
|
+
@to_graph[to] ||= Set.new
|
|
97
|
+
@to_graph[to].delete(from)
|
|
98
|
+
|
|
99
|
+
@roots.add(to) if @to_graph[to].empty?
|
|
100
|
+
|
|
101
|
+
invalidate_caches
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Adds the given vertex to the graph.
|
|
105
|
+
#
|
|
106
|
+
# @param v The vertex to add to the graph
|
|
107
|
+
#
|
|
108
|
+
# @return [void]
|
|
109
|
+
#
|
|
110
|
+
# @since 3.2.0
|
|
111
|
+
def add_vertex(v)
|
|
112
|
+
return if @vertices.include?(v)
|
|
113
|
+
|
|
114
|
+
@vertices << v
|
|
115
|
+
@vertice_indexes[v] = @vertices.size-1
|
|
116
|
+
|
|
117
|
+
@roots << v
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Deletes all edges coming from the given vertex.
|
|
121
|
+
#
|
|
122
|
+
# @param from Vertex from which all edges should be removed
|
|
123
|
+
#
|
|
124
|
+
# @return [void]
|
|
125
|
+
#
|
|
126
|
+
# @since 3.2.0
|
|
127
|
+
def delete_edges_from(from)
|
|
128
|
+
return if @from_graph[from].nil?
|
|
129
|
+
|
|
130
|
+
@from_graph[from].each do |to|
|
|
131
|
+
@to_graph[to].delete(from)
|
|
132
|
+
@roots.add(to) if @to_graph[to].empty?
|
|
133
|
+
end
|
|
134
|
+
@from_graph.delete(from)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Deletes all edges going to the given vertex.
|
|
138
|
+
#
|
|
139
|
+
# @param to Vertex to which all edges should be removed
|
|
140
|
+
#
|
|
141
|
+
# @return [void]
|
|
142
|
+
def delete_edges_to(to)
|
|
143
|
+
return if @to_graph[to].nil?
|
|
144
|
+
|
|
145
|
+
@to_graph[to].each do |from|
|
|
146
|
+
@from_graph[from].delete(to)
|
|
147
|
+
end
|
|
148
|
+
@to_graph.delete(to)
|
|
149
|
+
@roots.add(to)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Removes the given vertex from the graph.
|
|
153
|
+
#
|
|
154
|
+
# @param v Vertex to remove from the graph
|
|
155
|
+
#
|
|
156
|
+
# @return [void]
|
|
157
|
+
#
|
|
158
|
+
# @since 3.2.0
|
|
159
|
+
def delete_vertex(v)
|
|
160
|
+
delete_edges_to(v)
|
|
161
|
+
delete_edges_from(v)
|
|
162
|
+
|
|
163
|
+
@vertices.delete(v)
|
|
164
|
+
@roots.delete(v)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @group Querying the graph
|
|
168
|
+
|
|
169
|
+
# Returns the direct predecessors of the given vertex, i.e. the vertices
|
|
170
|
+
# x where there is an edge from x to the given vertex y.
|
|
171
|
+
#
|
|
172
|
+
# @param to The vertex of which the predecessors should be calculated
|
|
173
|
+
#
|
|
174
|
+
# @return [Array] Direct predecessors of the given vertex
|
|
175
|
+
def direct_predecessors_of(to)
|
|
176
|
+
@to_graph[to].to_a
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Returns the direct successors of the given vertex, i.e. the vertices y
|
|
180
|
+
# where there is an edge from the given vertex x to y.
|
|
181
|
+
#
|
|
182
|
+
# @param from The vertex of which the successors should be calculated
|
|
183
|
+
#
|
|
184
|
+
# @return [Array] Direct successors of the given vertex
|
|
185
|
+
def direct_successors_of(from)
|
|
186
|
+
@from_graph[from].to_a
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Returns the predecessors of the given vertex, i.e. the vertices x for
|
|
190
|
+
# which there is a path from x to the given vertex y.
|
|
191
|
+
#
|
|
192
|
+
# @param to The vertex of which the predecessors should be calculated
|
|
193
|
+
#
|
|
194
|
+
# @return [Array] Predecessors of the given vertex
|
|
195
|
+
def predecessors_of(to)
|
|
196
|
+
@predecessors[to] ||= recursively_find_vertices(to, :direct_predecessors_of)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Returns the successors of the given vertex, i.e. the vertices y for
|
|
200
|
+
# which there is a path from the given vertex x to y.
|
|
201
|
+
#
|
|
202
|
+
# @param from The vertex of which the successors should be calculated
|
|
203
|
+
#
|
|
204
|
+
# @return [Array] Successors of the given vertex
|
|
205
|
+
def successors_of(from)
|
|
206
|
+
@successors[from] ||= recursively_find_vertices(from, :direct_successors_of)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Returns an array of tuples representing the edges. The result of this
|
|
210
|
+
# method may take a while to compute and should be cached if possible.
|
|
211
|
+
#
|
|
212
|
+
# @return [Array] The list of all edges in this graph.
|
|
213
|
+
def edges
|
|
214
|
+
result = []
|
|
215
|
+
@vertices.each_with_index do |v, i|
|
|
216
|
+
direct_successors_of(v).map { |v2| @vertice_indexes[v2] }.each do |i2|
|
|
217
|
+
result << [ i, i2 ]
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
result
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Returns all root vertices, i.e. vertices where no edge points to.
|
|
224
|
+
#
|
|
225
|
+
# @return [Set] The set of all root vertices in this graph.
|
|
226
|
+
#
|
|
227
|
+
# @since 3.2.0
|
|
228
|
+
def roots
|
|
229
|
+
@roots
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# @group Deprecated methods
|
|
233
|
+
|
|
234
|
+
# @deprecated Use {#delete_edge} instead
|
|
235
|
+
def remove_edge(from, to)
|
|
236
|
+
delete_edge(from, to)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
private
|
|
240
|
+
|
|
241
|
+
# Invalidates cached data. This method should be called when the internal
|
|
242
|
+
# graph representation is changed.
|
|
243
|
+
def invalidate_caches
|
|
244
|
+
@predecessors = {}
|
|
245
|
+
@successors = {}
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Recursively finds vertices, starting at the vertex start, using the
|
|
249
|
+
# given method, which should be a symbol to a method that takes a vertex
|
|
250
|
+
# and returns related vertices (e.g. predecessors, successors).
|
|
251
|
+
def recursively_find_vertices(start, method)
|
|
252
|
+
all_vertices = Set.new
|
|
253
|
+
|
|
254
|
+
processed_vertices = Set.new
|
|
255
|
+
unprocessed_vertices = [ start ]
|
|
256
|
+
|
|
257
|
+
until unprocessed_vertices.empty?
|
|
258
|
+
# Get next unprocessed vertex
|
|
259
|
+
vertex = unprocessed_vertices.pop
|
|
260
|
+
next if processed_vertices.include?(vertex)
|
|
261
|
+
processed_vertices << vertex
|
|
262
|
+
|
|
263
|
+
# Add predecessors of this vertex
|
|
264
|
+
send(method, vertex).each do |v|
|
|
265
|
+
all_vertices << v unless all_vertices.include?(v)
|
|
266
|
+
unprocessed_vertices << v
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
all_vertices.to_a
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
end
|