nanoc 4.0.0b4 → 4.0.0rc1

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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +31 -32
  4. data/NEWS.md +7 -0
  5. data/README.md +1 -2
  6. data/Rakefile +2 -11
  7. data/TODO.md +0 -1
  8. data/bin/nanoc +4 -0
  9. data/lib/nanoc/base/checksummer.rb +1 -1
  10. data/lib/nanoc/base/compilation/compiler.rb +7 -13
  11. data/lib/nanoc/base/compilation/dependency_tracker.rb +1 -1
  12. data/lib/nanoc/base/compilation/rule.rb +2 -0
  13. data/lib/nanoc/base/compilation/rules_collection.rb +2 -2
  14. data/lib/nanoc/base/entities/identifier.rb +45 -15
  15. data/lib/nanoc/base/entities/pattern.rb +8 -0
  16. data/lib/nanoc/base/entities/snapshot_def.rb +16 -0
  17. data/lib/nanoc/base/entities.rb +1 -0
  18. data/lib/nanoc/base/plugin_registry.rb +1 -1
  19. data/lib/nanoc/base/{compilation → repos}/checksum_store.rb +0 -0
  20. data/lib/nanoc/base/{compilation → repos}/compiled_content_cache.rb +0 -0
  21. data/lib/nanoc/base/repos/config_loader.rb +67 -0
  22. data/lib/nanoc/base/{compilation → repos}/rule_memory_store.rb +0 -0
  23. data/lib/nanoc/base/repos/site_loader.rb +118 -0
  24. data/lib/nanoc/base/{store.rb → repos/store.rb} +0 -0
  25. data/lib/nanoc/base/repos.rb +7 -0
  26. data/lib/nanoc/base/result_data/item_rep.rb +37 -66
  27. data/lib/nanoc/base/services/executor.rb +15 -9
  28. data/lib/nanoc/base/services/item_rep_writer.rb +1 -1
  29. data/lib/nanoc/base/services/recording_executor.rb +4 -4
  30. data/lib/nanoc/base/source_data/configuration.rb +89 -3
  31. data/lib/nanoc/base/source_data/data_source.rb +2 -15
  32. data/lib/nanoc/base/source_data/site.rb +14 -378
  33. data/lib/nanoc/base/views/{config.rb → config_view.rb} +0 -0
  34. data/lib/nanoc/base/views/{identifiable_collection.rb → identifiable_collection_view.rb} +0 -0
  35. data/lib/nanoc/base/views/{item_collection.rb → item_collection_view.rb} +0 -0
  36. data/lib/nanoc/base/views/{item_rep_collection.rb → item_rep_collection_view.rb} +7 -1
  37. data/lib/nanoc/base/views/{item_rep.rb → item_rep_view.rb} +0 -0
  38. data/lib/nanoc/base/views/{item.rb → item_view.rb} +0 -0
  39. data/lib/nanoc/base/views/{layout_collection.rb → layout_collection_view.rb} +0 -0
  40. data/lib/nanoc/base/views/{layout.rb → layout_view.rb} +0 -0
  41. data/lib/nanoc/base/views/mixins/{document.rb → document_view_mixin.rb} +0 -0
  42. data/lib/nanoc/base/views/mixins/{mutable_document.rb → mutable_document_view_mixin.rb} +0 -0
  43. data/lib/nanoc/base/views/{mutable_config.rb → mutable_config_view.rb} +0 -0
  44. data/lib/nanoc/base/views/{mutable_identifiable_collection.rb → mutable_identifiable_collection_view.rb} +0 -0
  45. data/lib/nanoc/base/views/{mutable_item_collection.rb → mutable_item_collection_view.rb} +0 -0
  46. data/lib/nanoc/base/views/{mutable_item.rb → mutable_item_view.rb} +0 -0
  47. data/lib/nanoc/base/views/{mutable_layout_collection.rb → mutable_layout_collection_view.rb} +0 -0
  48. data/lib/nanoc/base/views/{mutable_layout.rb → mutable_layout_view.rb} +0 -0
  49. data/lib/nanoc/base/views/{site.rb → site_view.rb} +0 -0
  50. data/lib/nanoc/base/views.rb +17 -17
  51. data/lib/nanoc/base.rb +1 -4
  52. data/lib/nanoc/cli/ansi_string_colorizer.rb +1 -1
  53. data/lib/nanoc/cli/cleaning_stream.rb +5 -0
  54. data/lib/nanoc/cli/command_runner.rb +10 -4
  55. data/lib/nanoc/cli/commands/compile.rb +1 -1
  56. data/lib/nanoc/cli/commands/create-site.rb +12 -11
  57. data/lib/nanoc/cli/commands/prune.rb +1 -0
  58. data/lib/nanoc/cli/commands/show-plugins.rb +4 -4
  59. data/lib/nanoc/cli/commands/show-rules.rb +21 -30
  60. data/lib/nanoc/cli/commands/view.rb +1 -1
  61. data/lib/nanoc/cli/error_handler.rb +1 -1
  62. data/lib/nanoc/cli.rb +3 -3
  63. data/lib/nanoc/data_sources/filesystem.rb +4 -4
  64. data/lib/nanoc/extra/checking/runner.rb +3 -2
  65. data/lib/nanoc/extra/deployers/rsync.rb +1 -1
  66. data/lib/nanoc/extra/link_collector.rb +1 -1
  67. data/lib/nanoc/helpers/capturing.rb +1 -1
  68. data/lib/nanoc/helpers/rendering.rb +2 -2
  69. data/lib/nanoc/version.rb +1 -1
  70. data/nanoc.gemspec +3 -4
  71. data/tasks/doc.rake +1 -1
  72. data/tasks/rubocop.rake +4 -8
  73. data/tasks/test.rake +11 -27
  74. data/test/base/test_compiler.rb +21 -16
  75. data/test/base/test_compiler_dsl.rb +8 -8
  76. data/test/base/test_item.rb +1 -1
  77. data/test/base/test_item_rep.rb +16 -14
  78. data/test/base/test_memoization.rb +1 -38
  79. data/test/base/test_outdatedness_checker.rb +38 -6
  80. data/test/base/test_site.rb +19 -121
  81. data/test/cli/commands/test_compile.rb +1 -1
  82. data/test/cli/commands/test_create_site.rb +7 -8
  83. data/test/data_sources/test_filesystem.rb +7 -7
  84. data/test/data_sources/test_filesystem_unified.rb +17 -17
  85. data/test/extra/checking/checks/test_mixed_content.rb +8 -8
  86. data/test/extra/checking/checks/test_stale.rb +3 -4
  87. data/test/extra/deployers/test_fog.rb +1 -1
  88. data/test/extra/deployers/test_rsync.rb +2 -2
  89. data/test/extra/test_filesystem_tools.rb +1 -1
  90. data/test/filters/test_erb.rb +1 -1
  91. data/test/filters/test_handlebars.rb +2 -2
  92. data/test/filters/test_less.rb +2 -2
  93. data/test/filters/test_mustache.rb +2 -2
  94. data/test/filters/test_relativize_paths.rb +1 -1
  95. data/test/filters/test_sass.rb +6 -6
  96. data/test/filters/test_xsl.rb +3 -3
  97. data/test/helper.rb +10 -15
  98. data/test/helpers/test_blogging.rb +16 -16
  99. data/test/helpers/test_capturing.rb +6 -6
  100. data/test/helpers/test_link_to.rb +13 -13
  101. data/test/helpers/test_rendering.rb +33 -0
  102. data/test/helpers/test_tagging.rb +8 -8
  103. metadata +32 -30
  104. data/test/gem_loader.rb +0 -9
@@ -0,0 +1,118 @@
1
+ module Nanoc::Int
2
+ class SiteLoader
3
+ def new_empty
4
+ site_from_config(Nanoc::Int::Configuration.new.with_defaults)
5
+ end
6
+
7
+ def new_with_config(hash)
8
+ site_from_config(Nanoc::Int::Configuration.new(hash).with_defaults)
9
+ end
10
+
11
+ def new_from_cwd
12
+ site_from_config(Nanoc::Int::ConfigLoader.new.new_from_cwd)
13
+ end
14
+
15
+ # @api private
16
+ def setup_child_parent_links(items)
17
+ items.each do |item|
18
+ item.parent = nil
19
+ item.children = []
20
+ end
21
+
22
+ item_map = {}
23
+ items.each do |item|
24
+ next if item.identifier !~ /\/\z/
25
+ item_map[item.identifier.to_s] = item
26
+ end
27
+
28
+ items.each do |item|
29
+ parent_id_end = item.identifier.to_s.rindex('/', -2)
30
+ next unless parent_id_end
31
+
32
+ parent_id = item.identifier.to_s[0..parent_id_end]
33
+ parent = item_map[parent_id]
34
+ next unless parent
35
+
36
+ item.parent = parent
37
+ parent.children << item
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def site_from_config(config)
44
+ code_snippets = code_snippets_from_config(config)
45
+ code_snippets.each(&:load)
46
+
47
+ items = Nanoc::Int::IdentifiableCollection.new(config)
48
+ layouts = Nanoc::Int::IdentifiableCollection.new(config)
49
+
50
+ with_data_sources(config) do |data_sources|
51
+ data_sources.each do |ds|
52
+ items_in_ds = ds.items
53
+ layouts_in_ds = ds.layouts
54
+
55
+ items_in_ds.each { |i| i.identifier = i.identifier.prefix(ds.items_root) }
56
+ layouts_in_ds.each { |l| l.identifier = l.identifier.prefix(ds.layouts_root) }
57
+
58
+ items.concat(items_in_ds)
59
+ layouts.concat(layouts_in_ds)
60
+ end
61
+ end
62
+
63
+ setup_child_parent_links(items)
64
+
65
+ Nanoc::Int::Site.new(
66
+ config: config,
67
+ code_snippets: code_snippets,
68
+ items: items,
69
+ layouts: layouts,
70
+ )
71
+ end
72
+
73
+ # @return [Boolean]
74
+ def self.cwd_is_nanoc_site?
75
+ Nanoc::Int::ConfigLoader.cwd_is_nanoc_site?
76
+ end
77
+
78
+ def with_data_sources(config, &_block)
79
+ data_sources = create_data_sources(config)
80
+
81
+ begin
82
+ data_sources.each(&:use)
83
+ yield(data_sources)
84
+ ensure
85
+ data_sources.each(&:unuse)
86
+ end
87
+ end
88
+
89
+ def create_data_sources(config)
90
+ config[:data_sources].map do |data_source_hash|
91
+ # Get data source class
92
+ data_source_class = Nanoc::DataSource.named(data_source_hash[:type])
93
+ if data_source_class.nil?
94
+ raise Nanoc::Int::Errors::UnknownDataSource.new(data_source_hash[:type])
95
+ end
96
+
97
+ # Create data source
98
+ data_source_class.new(
99
+ config,
100
+ data_source_hash[:items_root],
101
+ data_source_hash[:layouts_root],
102
+ data_source_hash.merge(data_source_hash[:config] || {}),
103
+ )
104
+ end
105
+ end
106
+
107
+ def code_snippets_from_config(config)
108
+ config[:lib_dirs].flat_map do |lib|
109
+ Dir["#{lib}/**/*.rb"].sort.map do |filename|
110
+ Nanoc::Int::CodeSnippet.new(
111
+ File.read(filename),
112
+ filename,
113
+ )
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
File without changes
@@ -0,0 +1,7 @@
1
+ require_relative 'repos/store'
2
+
3
+ require_relative 'repos/checksum_store'
4
+ require_relative 'repos/compiled_content_cache'
5
+ require_relative 'repos/config_loader'
6
+ require_relative 'repos/rule_memory_store'
7
+ require_relative 'repos/site_loader'
@@ -1,70 +1,31 @@
1
1
  module Nanoc::Int
2
- # A single representation (rep) of an item ({Nanoc::Int::Item}). An item can
3
- # have multiple representations. A representation has its own output file.
4
- # A single item can therefore have multiple output files, each run through
5
- # a different set of filters with a different layout.
6
- #
7
2
  # @api private
8
3
  class ItemRep
9
- # Contains all private methods. Mixed into {Nanoc::Int::ItemRep}.
10
- #
11
- # @api private
12
- module Private
13
- attr_accessor :content_snapshots
14
-
15
- # @return [Boolean] true if this representation has already been
16
- # compiled during the current or last compilation session; false
17
- # otherwise
18
- #
19
- # @api private
20
- attr_accessor :compiled
21
- alias_method :compiled?, :compiled
22
-
23
- # @return [Hash<Symbol,String>] A hash containing the raw paths (paths
24
- # including the path to the output directory and the filename) for all
25
- # snapshots. The keys correspond with the snapshot names, and the
26
- # values with the path.
27
- #
28
- # @api private
29
- attr_accessor :raw_paths
30
-
31
- # @return [Hash<Symbol,String>] A hash containing the paths for all
32
- # snapshots. The keys correspond with the snapshot names, and the
33
- # values with the path.
34
- #
35
- # @api private
36
- attr_accessor :paths
37
-
38
- # Resets the compilation progress for this item representation. This is
39
- # necessary when an unmet dependency is detected during compilation.
40
- #
41
- # @api private
42
- #
43
- # @return [void]
44
- def forget_progress
45
- initialize_content
46
- end
47
- end
4
+ # @return [Hash<Symbol,Nanoc::Int::Content]
5
+ attr_accessor :snapshot_contents
48
6
 
49
- include Private
7
+ # @return [Boolean]
8
+ attr_accessor :compiled
9
+ alias_method :compiled?, :compiled
50
10
 
51
- # @return [Nanoc::Int::Item] The item to which this rep belongs
11
+ # @return [Hash<Symbol,String>]
12
+ attr_accessor :raw_paths
13
+
14
+ # @return [Hash<Symbol,String>]
15
+ attr_accessor :paths
16
+
17
+ # @return [Nanoc::Int::Item]
52
18
  attr_reader :item
53
19
 
54
- # @return [Symbol] The representation's unique name
20
+ # @return [Symbol]
55
21
  attr_reader :name
56
22
 
57
- # @return [Array] A list of snapshots, represented as arrays where the
58
- # first element is the snapshot name (a Symbol) and the last element is
59
- # a Boolean indicating whether the snapshot is final or not
60
- attr_accessor :snapshots
23
+ # @return [Enumerable<Nanoc::Int:SnapshotDef]
24
+ attr_accessor :snapshot_defs
61
25
 
62
- # Creates a new item representation for the given item.
63
- #
64
- # @param [Nanoc::Int::Item] item The item to which the new representation will
65
- # belong.
26
+ # @param [Nanoc::Int::Item] item
66
27
  #
67
- # @param [Symbol] name The unique name for the new item representation.
28
+ # @param [Symbol] name
68
29
  def initialize(item, name)
69
30
  # Set primary attributes
70
31
  @item = item
@@ -73,7 +34,7 @@ module Nanoc::Int
73
34
  # Set default attributes
74
35
  @raw_paths = {}
75
36
  @paths = {}
76
- @snapshots = []
37
+ @snapshot_defs = []
77
38
  initialize_content
78
39
 
79
40
  # Reset flags
@@ -81,7 +42,7 @@ module Nanoc::Int
81
42
  end
82
43
 
83
44
  def binary?
84
- @content_snapshots[:last].binary?
45
+ @snapshot_contents[:last].binary?
85
46
  end
86
47
 
87
48
  # Returns the compiled content from a given snapshot.
@@ -100,12 +61,12 @@ module Nanoc::Int
100
61
  end
101
62
 
102
63
  # Get name of last pre-layout snapshot
103
- snapshot_name = params.fetch(:snapshot) { @content_snapshots[:pre] ? :pre : :last }
64
+ snapshot_name = params.fetch(:snapshot) { @snapshot_contents[:pre] ? :pre : :last }
104
65
  is_moving = [:pre, :post, :last].include?(snapshot_name)
105
66
 
106
67
  # Check existance of snapshot
107
- snapshot = snapshots.find { |s| s.first == snapshot_name }
108
- if !is_moving && (snapshot.nil? || snapshot[-1] == false)
68
+ snapshot_def = snapshot_defs.find { |sd| sd.name == snapshot_name }
69
+ if !is_moving && (snapshot_def.nil? || !snapshot_def.final?)
109
70
  raise Nanoc::Int::Errors::NoSuchSnapshot.new(self, snapshot_name)
110
71
  end
111
72
 
@@ -115,14 +76,14 @@ module Nanoc::Int
115
76
  when :post, :last
116
77
  true
117
78
  when :pre
118
- snapshot.nil? || !snapshot[-1]
79
+ snapshot_def.nil? || !snapshot_def.final?
119
80
  end
120
- is_usable_snapshot = @content_snapshots[snapshot_name] && (self.compiled? || !is_still_moving)
81
+ is_usable_snapshot = @snapshot_contents[snapshot_name] && (self.compiled? || !is_still_moving)
121
82
  unless is_usable_snapshot
122
83
  raise Nanoc::Int::Errors::UnmetDependency.new(self)
123
84
  end
124
85
 
125
- @content_snapshots[snapshot_name].string
86
+ @snapshot_contents[snapshot_name].string
126
87
  end
127
88
 
128
89
  # Checks whether content exists at a given snapshot.
@@ -132,7 +93,7 @@ module Nanoc::Int
132
93
  #
133
94
  # @since 3.2.0
134
95
  def snapshot?(snapshot_name)
135
- !@content_snapshots[snapshot_name].nil?
96
+ !@snapshot_contents[snapshot_name].nil?
136
97
  end
137
98
  alias_method :has_snapshot?, :snapshot?
138
99
 
@@ -162,6 +123,16 @@ module Nanoc::Int
162
123
  @paths[snapshot_name]
163
124
  end
164
125
 
126
+ # Resets the compilation progress for this item representation. This is
127
+ # necessary when an unmet dependency is detected during compilation.
128
+ #
129
+ # @api private
130
+ #
131
+ # @return [void]
132
+ def forget_progress
133
+ initialize_content
134
+ end
135
+
165
136
  # Returns an object that can be used for uniquely identifying objects.
166
137
  #
167
138
  # @api private
@@ -179,7 +150,7 @@ module Nanoc::Int
179
150
 
180
151
  def initialize_content
181
152
  # FIXME: Where is :raw?
182
- @content_snapshots = { last: @item.content }
153
+ @snapshot_contents = { last: @item.content }
183
154
  end
184
155
  end
185
156
  end
@@ -1,6 +1,12 @@
1
1
  module Nanoc
2
2
  module Int
3
3
  class Executor
4
+ class OutputNotWrittenError < ::Nanoc::Error
5
+ def initialize(filter_name, output_filename)
6
+ super("The #{filter_name.inspect} filter did not write anything to the required output file, #{output_filename}.")
7
+ end
8
+ end
9
+
4
10
  def initialize(compiler)
5
11
  @compiler = compiler
6
12
  end
@@ -25,22 +31,22 @@ module Nanoc
25
31
  filter = klass.new(assigns_for(rep))
26
32
 
27
33
  # Run filter
28
- last = rep.content_snapshots[:last]
34
+ last = rep.snapshot_contents[:last]
29
35
  source = rep.binary? ? last.filename : last.string
30
36
  result = filter.setup_and_run(source, filter_args)
31
37
  if klass.to_binary?
32
- rep.content_snapshots[:last] = Nanoc::Int::BinaryContent.new(filter.output_filename).tap(&:freeze)
38
+ rep.snapshot_contents[:last] = Nanoc::Int::BinaryContent.new(filter.output_filename).tap(&:freeze)
33
39
  else
34
- rep.content_snapshots[:last] = Nanoc::Int::TextualContent.new(result).tap(&:freeze)
40
+ rep.snapshot_contents[:last] = Nanoc::Int::TextualContent.new(result).tap(&:freeze)
35
41
  end
36
42
 
37
43
  # Check whether file was written
38
44
  if klass.to_binary? && !File.file?(filter.output_filename)
39
- raise "The #{filter_name.inspect} filter did not write anything to the required output file, #{filter.output_filename}."
45
+ raise OutputNotWrittenError.new(filter_name, filter.output_filename)
40
46
  end
41
47
 
42
48
  # Create snapshot
43
- snapshot(rep, rep.content_snapshots[:post] ? :post : :pre, final: false) unless rep.binary?
49
+ snapshot(rep, rep.snapshot_contents[:post] ? :post : :pre, final: false) unless rep.binary?
44
50
  ensure
45
51
  # Notify end
46
52
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, filter_name)
@@ -59,7 +65,7 @@ module Nanoc
59
65
  raise Nanoc::Int::Errors::CannotLayoutBinaryItem.new(rep) if rep.binary?
60
66
 
61
67
  # Create "pre" snapshot
62
- if rep.content_snapshots[:post].nil?
68
+ if rep.snapshot_contents[:post].nil?
63
69
  snapshot(rep, :pre, final: true)
64
70
  end
65
71
 
@@ -81,7 +87,7 @@ module Nanoc
81
87
  content = layout.content
82
88
  arg = content.binary? ? content.filename : content.string
83
89
  res = filter.setup_and_run(arg, filter_args)
84
- rep.content_snapshots[:last] = Nanoc::Int::TextualContent.new(res).tap(&:freeze)
90
+ rep.snapshot_contents[:last] = Nanoc::Int::TextualContent.new(res).tap(&:freeze)
85
91
 
86
92
  # Create "post" snapshot
87
93
  snapshot(rep, :post, final: false)
@@ -96,11 +102,11 @@ module Nanoc
96
102
  is_final = params.fetch(:final, true)
97
103
 
98
104
  unless rep.binary?
99
- rep.content_snapshots[snapshot_name] = rep.content_snapshots[:last]
105
+ rep.snapshot_contents[snapshot_name] = rep.snapshot_contents[:last]
100
106
  end
101
107
 
102
108
  if snapshot_name == :pre && is_final
103
- rep.snapshots << [:pre, true]
109
+ rep.snapshot_defs << Nanoc::Int::SnapshotDef.new(:pre, true)
104
110
  end
105
111
 
106
112
  if is_final
@@ -14,7 +14,7 @@ module Nanoc::Int
14
14
  Nanoc::Int::NotificationCenter.post(
15
15
  :will_write_rep, item_rep, raw_path)
16
16
 
17
- content = item_rep.content_snapshots[:last]
17
+ content = item_rep.snapshot_contents[:last]
18
18
  if content.binary?
19
19
  temp_path = content.filename
20
20
  else
@@ -7,11 +7,11 @@ module Nanoc
7
7
  @rule_memory = []
8
8
  end
9
9
 
10
- def filter(rep, filter_name, filter_args = {})
10
+ def filter(_rep, filter_name, filter_args = {})
11
11
  @rule_memory << [:filter, filter_name, filter_args]
12
12
  end
13
13
 
14
- def layout(rep, layout_identifier, extra_filter_args = nil)
14
+ def layout(_rep, layout_identifier, extra_filter_args = nil)
15
15
  if extra_filter_args
16
16
  @rule_memory << [:layout, layout_identifier, extra_filter_args]
17
17
  else
@@ -19,7 +19,7 @@ module Nanoc
19
19
  end
20
20
  end
21
21
 
22
- def snapshot(rep, snapshot_name, params = {})
22
+ def snapshot(_rep, snapshot_name, params = {})
23
23
  @rule_memory << [:snapshot, snapshot_name, params]
24
24
 
25
25
  # Count
@@ -33,7 +33,7 @@ module Nanoc
33
33
  end
34
34
  end
35
35
 
36
- def record_write(rep, path)
36
+ def record_write(_rep, path)
37
37
  @rule_memory << [:write, path]
38
38
  end
39
39
  end
@@ -2,12 +2,94 @@ module Nanoc::Int
2
2
  # Represents the site configuration.
3
3
  #
4
4
  # @api private
5
- class Configuration < ::Hash
5
+ class Configuration
6
+ NONE = Object.new
7
+
8
+ # The default configuration for a data source. A data source's
9
+ # configuration overrides these options.
10
+ DEFAULT_DATA_SOURCE_CONFIG = {
11
+ type: 'filesystem',
12
+ items_root: '/',
13
+ layouts_root: '/',
14
+ config: {},
15
+ identifier_type: 'full',
16
+ }
17
+
18
+ # The default configuration for a site. A site's configuration overrides
19
+ # these options: when a {Nanoc::Int::Site} is created with a configuration
20
+ # that lacks some options, the default value will be taken from
21
+ # `DEFAULT_CONFIG`.
22
+ DEFAULT_CONFIG = {
23
+ text_extensions: %w( css erb haml htm html js less markdown md php rb sass scss txt xhtml xml coffee hb handlebars mustache ms slim rdoc ).sort,
24
+ lib_dirs: %w( lib ),
25
+ commands_dirs: %w( commands ),
26
+ output_dir: 'output',
27
+ data_sources: [{}],
28
+ index_filenames: ['index.html'],
29
+ enable_output_diff: false,
30
+ prune: { auto_prune: false, exclude: ['.git', '.hg', '.svn', 'CVS'] },
31
+ string_pattern_type: 'glob',
32
+ }
33
+
6
34
  # Creates a new configuration with the given hash.
7
35
  #
8
36
  # @param [Hash] hash The actual configuration hash
9
- def initialize(hash)
10
- replace(hash)
37
+ def initialize(hash = {})
38
+ @wrapped = hash.__nanoc_symbolize_keys_recursively
39
+ end
40
+
41
+ def with_defaults
42
+ new_wrapped = DEFAULT_CONFIG.merge(@wrapped)
43
+ new_wrapped[:data_sources] = new_wrapped[:data_sources].map do |ds|
44
+ DEFAULT_DATA_SOURCE_CONFIG.merge(ds)
45
+ end
46
+
47
+ self.class.new(new_wrapped)
48
+ end
49
+
50
+ def to_h
51
+ @wrapped
52
+ end
53
+
54
+ def [](key)
55
+ @wrapped[key]
56
+ end
57
+
58
+ def fetch(key, fallback = NONE, &_block)
59
+ @wrapped.fetch(key) do
60
+ if !fallback.equal?(NONE)
61
+ fallback
62
+ elsif block_given?
63
+ yield(key)
64
+ else
65
+ raise KeyError, "key not found: #{key.inspect}"
66
+ end
67
+ end
68
+ end
69
+
70
+ def []=(key, value)
71
+ @wrapped[key] = value
72
+ end
73
+
74
+ def merge(hash)
75
+ self.class.new(@wrapped.merge(hash.to_h))
76
+ end
77
+
78
+ def without(key)
79
+ self.class.new(@wrapped.reject { |k, _v| k == key })
80
+ end
81
+
82
+ def update(hash)
83
+ @wrapped.update(hash)
84
+ end
85
+
86
+ def each
87
+ @wrapped.each { |k, v| yield(k, v) }
88
+ self
89
+ end
90
+
91
+ def __nanoc_freeze_recursively
92
+ @wrapped.__nanoc_freeze_recursively
11
93
  end
12
94
 
13
95
  # Returns an object that can be used for uniquely identifying objects.
@@ -16,5 +98,9 @@ module Nanoc::Int
16
98
  def reference
17
99
  :config
18
100
  end
101
+
102
+ def inspect
103
+ "<#{self.class}>"
104
+ end
19
105
  end
20
106
  end
@@ -29,21 +29,8 @@ module Nanoc
29
29
 
30
30
  extend Nanoc::Int::PluginRegistry::PluginMethods
31
31
 
32
- # Creates a new data source for the given site.
33
- #
34
- # @param [Nanoc::Int::Site] site The site this data source belongs to.
35
- #
36
- # @param [String] items_root The prefix that should be given to all items
37
- # returned by the #items method (comparable to mount points for
38
- # filesystems in Unix-ish OSes).
39
- #
40
- # @param [String] layouts_root The prefix that should be given to all
41
- # layouts returned by the #layouts method (comparable to mount points
42
- # for filesystems in Unix-ish OSes).
43
- #
44
- # @param [Hash] config The configuration for this data source.
45
- def initialize(site, items_root, layouts_root, config)
46
- @site = site
32
+ def initialize(site_config, items_root, layouts_root, config)
33
+ @site_config = site_config
47
34
  @items_root = items_root
48
35
  @layouts_root = layouts_root
49
36
  @config = config || {}