nanoc-core 4.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/NEWS.md +3 -0
  3. data/README.md +3 -0
  4. data/lib/nanoc/core/binary_content.rb +12 -0
  5. data/lib/nanoc/core/checksummer.rb +287 -0
  6. data/lib/nanoc/core/code_snippet.rb +57 -0
  7. data/lib/nanoc/core/configuration-schema.json +122 -0
  8. data/lib/nanoc/core/configuration.rb +206 -0
  9. data/lib/nanoc/core/content.rb +46 -0
  10. data/lib/nanoc/core/context.rb +70 -0
  11. data/lib/nanoc/core/contracts_support.rb +131 -0
  12. data/lib/nanoc/core/core_ext/array.rb +54 -0
  13. data/lib/nanoc/core/core_ext/hash.rb +58 -0
  14. data/lib/nanoc/core/core_ext/string.rb +20 -0
  15. data/lib/nanoc/core/data_source.rb +170 -0
  16. data/lib/nanoc/core/directed_graph.rb +195 -0
  17. data/lib/nanoc/core/document.rb +124 -0
  18. data/lib/nanoc/core/error.rb +9 -0
  19. data/lib/nanoc/core/identifiable_collection.rb +142 -0
  20. data/lib/nanoc/core/identifier.rb +218 -0
  21. data/lib/nanoc/core/item.rb +11 -0
  22. data/lib/nanoc/core/item_collection.rb +15 -0
  23. data/lib/nanoc/core/item_rep.rb +92 -0
  24. data/lib/nanoc/core/layout.rb +11 -0
  25. data/lib/nanoc/core/layout_collection.rb +15 -0
  26. data/lib/nanoc/core/lazy_value.rb +38 -0
  27. data/lib/nanoc/core/notification_center.rb +97 -0
  28. data/lib/nanoc/core/pattern.rb +37 -0
  29. data/lib/nanoc/core/processing_action.rb +23 -0
  30. data/lib/nanoc/core/processing_actions/filter.rb +40 -0
  31. data/lib/nanoc/core/processing_actions/layout.rb +40 -0
  32. data/lib/nanoc/core/processing_actions/snapshot.rb +50 -0
  33. data/lib/nanoc/core/processing_actions.rb +12 -0
  34. data/lib/nanoc/core/regexp_pattern.rb +28 -0
  35. data/lib/nanoc/core/snapshot_def.rb +22 -0
  36. data/lib/nanoc/core/string_pattern.rb +29 -0
  37. data/lib/nanoc/core/temp_filename_factory.rb +53 -0
  38. data/lib/nanoc/core/textual_content.rb +41 -0
  39. data/lib/nanoc/core/version.rb +7 -0
  40. data/lib/nanoc/core.rb +60 -0
  41. data/lib/nanoc-core.rb +3 -0
  42. metadata +152 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6b14c2953e85fe025ebc7b886e49e7c5479f9a2a5e4ae490b427760ed916646b
4
+ data.tar.gz: cdc17c42152a6ef9474e1406d1f2078884b298833213707b54cd2825785fe448
5
+ SHA512:
6
+ metadata.gz: 0b039e4fdf615feb7cacd556600fa3cf967290335d2034cbe9046b59613ea0e8b736078aa83ca2fc2c921869ee50cf4e62f5fa823341674612ae72801df53dbc
7
+ data.tar.gz: e1b1374ba0c0bd461de1ccb8ec658dbe65f49c9b0eae2a48efe138757cea4f0f66c0a3b79e0243b9cbc6d6ad312b6a589dbbd3f620bea0948da37b79b82d156d
data/NEWS.md ADDED
@@ -0,0 +1,3 @@
1
+ # nanoc-core news
2
+
3
+ See the release notes for Nanoc for details.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # nanoc-core
2
+
3
+ This repository contains the core of Nanoc. It is designed to have no dependencies (neither gems nor external applications), and be operating system-agnostic.
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class BinaryContent < Content
6
+ contract C::None => C::Bool
7
+ def binary?
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Creates checksums for given objects.
6
+ #
7
+ # A checksum is a string, such as “mL+TaqNsEeiPkWloPgCtAofT1yg=”, that is used
8
+ # to determine whether a piece of data has changed.
9
+ class Checksummer
10
+ class VerboseDigest
11
+ def initialize
12
+ @str = +''
13
+ end
14
+
15
+ def update(str)
16
+ @str << str
17
+ end
18
+
19
+ def to_s
20
+ @str
21
+ end
22
+ end
23
+
24
+ class CompactDigest
25
+ def initialize
26
+ @digest = Digest::SHA1.new
27
+ end
28
+
29
+ def update(str)
30
+ @digest.update(str)
31
+ end
32
+
33
+ def to_s
34
+ @digest.base64digest
35
+ end
36
+ end
37
+
38
+ class << self
39
+ # @param obj The object to create a checksum for
40
+ #
41
+ # @return [String] The digest
42
+ def calc(obj, digest_class = CompactDigest)
43
+ digest = digest_class.new
44
+ update(obj, digest)
45
+ digest.to_s
46
+ end
47
+
48
+ def calc_for_content_of(obj)
49
+ obj.content_checksum_data || obj.checksum_data || Nanoc::Core::Checksummer.calc(obj.content)
50
+ end
51
+
52
+ def calc_for_each_attribute_of(obj, digest_class = CompactDigest)
53
+ obj.attributes.each_with_object({}) do |(key, value), memo|
54
+ memo[key] = Nanoc::Core::Checksummer.calc(value, digest_class)
55
+ end
56
+ end
57
+
58
+ def define_behavior(klass, behavior)
59
+ behaviors[klass] = behavior
60
+ end
61
+
62
+ private
63
+
64
+ def update(obj, digest, visited = Hamster::Set.new)
65
+ digest.update(obj.class.to_s)
66
+
67
+ if visited.include?(obj)
68
+ digest.update('<recur>')
69
+ else
70
+ digest.update('<')
71
+ behavior_for(obj).update(obj, digest) { |o| update(o, digest, visited.add(obj)) }
72
+ digest.update('>')
73
+ end
74
+ end
75
+
76
+ def behaviors
77
+ return @behaviors if @behaviors
78
+
79
+ @behaviors = {}
80
+
81
+ # NOTE: Other behaviors are registered elsewhere
82
+ # (search for `define_behavior`).
83
+
84
+ define_behavior(Array, ArrayUpdateBehavior)
85
+ define_behavior(FalseClass, NoUpdateBehavior)
86
+ define_behavior(Hash, HashUpdateBehavior)
87
+ define_behavior(NilClass, NoUpdateBehavior)
88
+ define_behavior(Numeric, RawUpdateBehavior)
89
+ define_behavior(Pathname, PathnameUpdateBehavior)
90
+ define_behavior(String, RawUpdateBehavior)
91
+ define_behavior(Symbol, RawUpdateBehavior)
92
+ define_behavior(Time, ToIToSUpdateBehavior)
93
+ define_behavior(TrueClass, NoUpdateBehavior)
94
+
95
+ define_behavior(Nanoc::Core::BinaryContent, BinaryContentUpdateBehavior)
96
+ define_behavior(Nanoc::Core::Configuration, HashUpdateBehavior)
97
+ define_behavior(Nanoc::Core::Context, ContextUpdateBehavior)
98
+ define_behavior(Nanoc::Core::IdentifiableCollection, ArrayUpdateBehavior)
99
+ define_behavior(Nanoc::Core::Identifier, ToSUpdateBehavior)
100
+ define_behavior(Nanoc::Core::Item, DocumentUpdateBehavior)
101
+ define_behavior(Nanoc::Core::ItemRep, ItemRepUpdateBehavior)
102
+ define_behavior(Nanoc::Core::Layout, DocumentUpdateBehavior)
103
+ define_behavior(Nanoc::Core::TextualContent, StringUpdateBehavior)
104
+
105
+ @behaviors
106
+ end
107
+
108
+ def behavior_for_class(klass)
109
+ behaviors.fetch(klass) do
110
+ if Object.equal?(klass.superclass)
111
+ RescueUpdateBehavior
112
+ else
113
+ behavior_for_class(klass.superclass)
114
+ end
115
+ end
116
+ end
117
+
118
+ def behavior_for(obj)
119
+ behavior_for_class(obj.class)
120
+ end
121
+ end
122
+
123
+ class UpdateBehavior
124
+ def self.update(_obj, _digest)
125
+ raise NotImpementedError
126
+ end
127
+ end
128
+
129
+ class RuleContextUpdateBehavior < UpdateBehavior
130
+ def self.update(obj, digest)
131
+ digest.update('item=')
132
+ yield(obj.item)
133
+ digest.update(',rep=')
134
+ yield(obj.rep)
135
+ digest.update(',items=')
136
+ yield(obj.items)
137
+ digest.update(',layouts=')
138
+ yield(obj.layouts)
139
+ digest.update(',config=')
140
+ yield(obj.config)
141
+ end
142
+ end
143
+
144
+ class ContextUpdateBehavior < UpdateBehavior
145
+ def self.update(obj, digest)
146
+ obj.instance_variables.each do |var|
147
+ digest.update(var.to_s)
148
+ digest.update('=')
149
+ yield(obj.instance_variable_get(var))
150
+ digest.update(',')
151
+ end
152
+ end
153
+ end
154
+
155
+ class RawUpdateBehavior < UpdateBehavior
156
+ def self.update(obj, digest)
157
+ digest.update(obj.to_s)
158
+ end
159
+ end
160
+
161
+ class ToSUpdateBehavior < UpdateBehavior
162
+ def self.update(obj, _digest)
163
+ yield(obj.to_s)
164
+ end
165
+ end
166
+
167
+ class ToIToSUpdateBehavior < UpdateBehavior
168
+ def self.update(obj, digest)
169
+ digest.update(obj.to_i.to_s)
170
+ end
171
+ end
172
+
173
+ class StringUpdateBehavior < UpdateBehavior
174
+ def self.update(obj, _digest)
175
+ yield(obj.string)
176
+ end
177
+ end
178
+
179
+ class DataUpdateBehavior < UpdateBehavior
180
+ def self.update(obj, _digest)
181
+ yield(obj.data)
182
+ end
183
+ end
184
+
185
+ class NoUpdateBehavior < UpdateBehavior
186
+ def self.update(_obj, _digest); end
187
+ end
188
+
189
+ class UnwrapUpdateBehavior < UpdateBehavior
190
+ def self.update(obj, _digest)
191
+ yield(obj._unwrap)
192
+ end
193
+ end
194
+
195
+ class ArrayUpdateBehavior < UpdateBehavior
196
+ def self.update(obj, digest)
197
+ obj.each do |el|
198
+ yield(el)
199
+ digest.update(',')
200
+ end
201
+ end
202
+ end
203
+
204
+ class HashUpdateBehavior < UpdateBehavior
205
+ def self.update(obj, digest)
206
+ obj.each do |key, value|
207
+ yield(key)
208
+ digest.update('=')
209
+ yield(value)
210
+ digest.update(',')
211
+ end
212
+ end
213
+ end
214
+
215
+ class DocumentUpdateBehavior < UpdateBehavior
216
+ def self.update(obj, digest)
217
+ if obj.checksum_data
218
+ digest.update('checksum_data=' + obj.checksum_data)
219
+ else
220
+ if obj.content_checksum_data
221
+ digest.update('content_checksum_data=' + obj.content_checksum_data)
222
+ else
223
+ digest.update('content=')
224
+ yield(obj.content)
225
+ end
226
+
227
+ if obj.attributes_checksum_data
228
+ digest.update(',attributes_checksum_data=' + obj.attributes_checksum_data)
229
+ else
230
+ digest.update(',attributes=')
231
+ yield(obj.attributes)
232
+ end
233
+
234
+ digest.update(',identifier=')
235
+ yield(obj.identifier)
236
+ end
237
+ end
238
+ end
239
+
240
+ class ItemRepUpdateBehavior < UpdateBehavior
241
+ def self.update(obj, digest)
242
+ digest.update('item=')
243
+ yield(obj.item)
244
+ digest.update(',name=')
245
+ yield(obj.name)
246
+ end
247
+ end
248
+
249
+ class PathnameUpdateBehavior < UpdateBehavior
250
+ def self.update(obj, digest)
251
+ filename = obj.to_s
252
+ if File.exist?(filename)
253
+ stat = File.stat(filename)
254
+ digest.update(stat.size.to_s + '-' + stat.mtime.to_i.to_s)
255
+ else
256
+ digest.update('???')
257
+ end
258
+ end
259
+ end
260
+
261
+ class BinaryContentUpdateBehavior < UpdateBehavior
262
+ def self.update(obj, _digest)
263
+ yield(Pathname.new(obj.filename))
264
+ end
265
+ end
266
+
267
+ class RescueUpdateBehavior < UpdateBehavior
268
+ def self.update(obj, digest)
269
+ if obj.class.to_s == 'Sass::Importers::Filesystem'
270
+ digest.update('root=')
271
+ digest.update(obj.root)
272
+ return
273
+ end
274
+
275
+ data =
276
+ begin
277
+ Marshal.dump(obj)
278
+ rescue
279
+ obj.inspect
280
+ end
281
+
282
+ digest.update(data)
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Nanoc::Core::CodeSnippet represent a piece of custom code of a Nanoc site.
6
+ #
7
+ # @api private
8
+ class CodeSnippet
9
+ include Nanoc::Core::ContractsSupport
10
+
11
+ # A string containing the actual code in this code snippet.
12
+ #
13
+ # @return [String]
14
+ attr_reader :data
15
+
16
+ # The filename corresponding to this code snippet.
17
+ #
18
+ # @return [String]
19
+ attr_reader :filename
20
+
21
+ contract String, String => C::Any
22
+ # Creates a new code snippet.
23
+ #
24
+ # @param [String] data The raw source code which will be executed before
25
+ # compilation
26
+ #
27
+ # @param [String] filename The filename corresponding to this code snippet
28
+ def initialize(data, filename)
29
+ @data = data
30
+ @filename = filename
31
+ end
32
+
33
+ contract C::None => nil
34
+ # Loads the code by executing it.
35
+ #
36
+ # @return [void]
37
+ def load
38
+ # rubocop:disable Security/Eval
39
+ eval('def self.use_helper(mod); Nanoc::Core::Context.instance_eval { include mod }; end', TOPLEVEL_BINDING)
40
+ eval(@data, TOPLEVEL_BINDING, @filename)
41
+ # rubocop:enable Security/Eval
42
+ nil
43
+ end
44
+
45
+ # Returns an object that can be used for uniquely identifying objects.
46
+ #
47
+ # @return [Object] An unique reference to this object
48
+ def reference
49
+ "code_snippet:#{filename}"
50
+ end
51
+
52
+ def inspect
53
+ "<#{self.class} filename=\"#{filename}\">"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,122 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "Nanoc configuration schema",
4
+ "type": "object",
5
+ "properties": {
6
+ "text_extensions": {
7
+ "type": "array",
8
+ "items": {
9
+ "type": "string"
10
+ }
11
+ },
12
+ "output_dir": {
13
+ "type": "string"
14
+ },
15
+ "index_filenames": {
16
+ "type": "array",
17
+ "items": {
18
+ "type": "string"
19
+ }
20
+ },
21
+ "enable_output_diff": {
22
+ "type": "boolean"
23
+ },
24
+ "prune": {
25
+ "type": "object",
26
+ "additionalProperties": false,
27
+ "properties": {
28
+ "auto_prune": {
29
+ "type": "boolean"
30
+ },
31
+ "exclude": {
32
+ "type": "array",
33
+ "items": {
34
+ "type": "string"
35
+ }
36
+ }
37
+ }
38
+ },
39
+ "commands_dirs": {
40
+ "type": "array",
41
+ "items": {
42
+ "type": "string"
43
+ }
44
+ },
45
+ "lib_dirs": {
46
+ "type": "array",
47
+ "items": {
48
+ "type": "string"
49
+ }
50
+ },
51
+ "data_sources": {
52
+ "type": "array",
53
+ "items": {
54
+ "type": "object",
55
+ "properties": {
56
+ "type": {
57
+ "type": "string"
58
+ },
59
+ "items_root": {
60
+ "anyOf": [
61
+ { "type": "string" },
62
+ { "type": "null" }
63
+ ]
64
+ },
65
+ "layouts_root": {
66
+ "anyOf": [
67
+ { "type": "string" },
68
+ { "type": "null" }
69
+ ]
70
+ }
71
+ }
72
+ }
73
+ },
74
+ "string_pattern_type": {
75
+ "type": "string",
76
+ "enum": ["glob", "legacy"]
77
+ },
78
+ "checks": {
79
+ "type": "object",
80
+ "properties": {
81
+ "internal_links": {
82
+ "type": "object",
83
+ "additionalProperties": false,
84
+ "properties": {
85
+ "exclude": {
86
+ "type": "array",
87
+ "items": {
88
+ "type": "string"
89
+ }
90
+ }
91
+ }
92
+ },
93
+ "external_links": {
94
+ "type": "object",
95
+ "additionalProperties": false,
96
+ "properties": {
97
+ "exclude": {
98
+ "type": "array",
99
+ "items": {
100
+ "type": "string"
101
+ }
102
+ },
103
+ "exclude_files": {
104
+ "type": "array",
105
+ "items": {
106
+ "type": "string"
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ },
113
+ "environments": {
114
+ "type": "object",
115
+ "patternProperties": {
116
+ "^.*$": {
117
+ "type": "object"
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Represents the site configuration.
6
+ class Configuration
7
+ include Nanoc::Core::ContractsSupport
8
+
9
+ NONE = Object.new.freeze
10
+
11
+ # The default configuration for a data source. A data source's
12
+ # configuration overrides these options.
13
+ DEFAULT_DATA_SOURCE_CONFIG = {
14
+ type: 'filesystem',
15
+ items_root: '/',
16
+ layouts_root: '/',
17
+ config: {},
18
+ identifier_type: 'full',
19
+ }.freeze
20
+
21
+ # The default configuration for a site. A site's configuration overrides
22
+ # these options: when a {Nanoc::Int::Site} is created with a configuration
23
+ # that lacks some options, the default value will be taken from
24
+ # `DEFAULT_CONFIG`.
25
+ DEFAULT_CONFIG = {
26
+ text_extensions: %w[adoc asciidoc atom css erb haml htm html js less markdown md php rb sass scss tex txt xhtml xml coffee hb handlebars mustache ms slim rdoc].sort,
27
+ lib_dirs: %w[lib],
28
+ commands_dirs: %w[commands],
29
+ output_dir: 'output',
30
+ data_sources: [{}],
31
+ index_filenames: ['index.html'],
32
+ enable_output_diff: false,
33
+ prune: { auto_prune: false, exclude: ['.git', '.hg', '.svn', 'CVS'] },
34
+ string_pattern_type: 'glob',
35
+ action_provider: 'rule_dsl',
36
+ }.freeze
37
+
38
+ # @return [String, nil] The active environment for the configuration
39
+ attr_reader :env_name
40
+
41
+ contract C::None => C::AbsolutePathString
42
+ attr_reader :dir
43
+
44
+ # Configuration environments property key
45
+ ENVIRONMENTS_CONFIG_KEY = :environments
46
+ NANOC_ENV = 'NANOC_ENV'
47
+ NANOC_ENV_DEFAULT = 'default'
48
+
49
+ contract C::KeywordArgs[hash: C::Optional[Hash], env_name: C::Maybe[String], dir: C::AbsolutePathString] => C::Any
50
+ def initialize(hash: {}, dir:, env_name: nil)
51
+ @env_name = env_name
52
+ @wrapped = hash.__nanoc_symbolize_keys_recursively
53
+ @dir = dir
54
+
55
+ validate
56
+ end
57
+
58
+ contract C::None => self
59
+ def with_defaults
60
+ new_wrapped = DEFAULT_CONFIG.merge(@wrapped)
61
+ new_wrapped[:data_sources] = new_wrapped[:data_sources].map do |ds|
62
+ DEFAULT_DATA_SOURCE_CONFIG.merge(ds)
63
+ end
64
+
65
+ self.class.new(hash: new_wrapped, dir: @dir, env_name: @env_name)
66
+ end
67
+
68
+ def with_environment
69
+ return self unless @wrapped.key?(ENVIRONMENTS_CONFIG_KEY)
70
+
71
+ # Set active environment
72
+ env_name = @env_name || ENV.fetch(NANOC_ENV, NANOC_ENV_DEFAULT)
73
+
74
+ # Load given environment configuration
75
+ env_config = @wrapped[ENVIRONMENTS_CONFIG_KEY].fetch(env_name.to_sym, {})
76
+
77
+ self.class.new(hash: @wrapped, dir: @dir, env_name: env_name).merge(env_config)
78
+ end
79
+
80
+ contract C::None => Hash
81
+ def to_h
82
+ @wrapped
83
+ end
84
+
85
+ # For compat
86
+ contract C::None => Hash
87
+ def attributes
88
+ to_h
89
+ end
90
+
91
+ contract C::Any => C::Bool
92
+ def key?(key)
93
+ @wrapped.key?(key)
94
+ end
95
+
96
+ contract C::Any => C::Any
97
+ def [](key)
98
+ @wrapped[key]
99
+ end
100
+
101
+ contract C::Args[C::Any] => C::Any
102
+ def dig(*keys)
103
+ @wrapped.dig(*keys)
104
+ end
105
+
106
+ contract C::Any, C::Maybe[C::Any], C::Maybe[C::Func[C::None => C::Any]] => C::Any
107
+ def fetch(key, fallback = NONE, &_block)
108
+ @wrapped.fetch(key) do
109
+ if !fallback.equal?(NONE)
110
+ fallback
111
+ elsif block_given?
112
+ yield(key)
113
+ else
114
+ raise KeyError, "key not found: #{key.inspect}"
115
+ end
116
+ end
117
+ end
118
+
119
+ contract C::Any, C::Any => C::Any
120
+ def []=(key, value)
121
+ @wrapped[key] = value
122
+ end
123
+
124
+ contract C::Or[Hash, self] => self
125
+ def merge(hash)
126
+ self.class.new(hash: merge_recursively(@wrapped, hash.to_h), dir: @dir, env_name: @env_name)
127
+ end
128
+
129
+ contract C::Any => self
130
+ def without(key)
131
+ self.class.new(hash: @wrapped.reject { |k, _v| k == key }, dir: @dir, env_name: @env_name)
132
+ end
133
+
134
+ contract C::Any => self
135
+ def update(hash)
136
+ @wrapped.update(hash)
137
+ self
138
+ end
139
+
140
+ contract C::Func[C::Any, C::Any => C::Any] => self
141
+ def each
142
+ @wrapped.each { |k, v| yield(k, v) }
143
+ self
144
+ end
145
+
146
+ contract C::None => self
147
+ def freeze
148
+ super
149
+ @wrapped.__nanoc_freeze_recursively
150
+ self
151
+ end
152
+
153
+ contract C::None => C::AbsolutePathString
154
+ def output_dir
155
+ make_absolute(self[:output_dir]).freeze
156
+ end
157
+
158
+ contract C::None => Symbol
159
+ def action_provider
160
+ self[:action_provider].to_sym
161
+ end
162
+
163
+ contract C::None => C::IterOf[C::AbsolutePathString]
164
+ def output_dirs
165
+ envs = @wrapped.fetch(ENVIRONMENTS_CONFIG_KEY, {})
166
+ res = [output_dir] + envs.values.map { |v| make_absolute(v[:output_dir]) }
167
+ res.uniq.compact
168
+ end
169
+
170
+ # Returns an object that can be used for uniquely identifying objects.
171
+ #
172
+ # @return [Object] An unique reference to this object
173
+ def reference
174
+ 'configuration'
175
+ end
176
+
177
+ def inspect
178
+ "<#{self.class}>"
179
+ end
180
+
181
+ private
182
+
183
+ def make_absolute(path)
184
+ path && @dir && File.absolute_path(path, @dir).encode('UTF-8')
185
+ end
186
+
187
+ def merge_recursively(config1, config2)
188
+ config1.merge(config2) do |_, value1, value2|
189
+ if value1.is_a?(Hash) && value2.is_a?(Hash)
190
+ merge_recursively(value1, value2)
191
+ else
192
+ value2
193
+ end
194
+ end
195
+ end
196
+
197
+ def validate
198
+ dir = File.dirname(__FILE__)
199
+ schema_data = JSON.parse(File.read(dir + '/configuration-schema.json'))
200
+ schema = JsonSchema.parse!(schema_data)
201
+ schema.expand_references!
202
+ schema.validate!(@wrapped.__nanoc_stringify_keys_recursively)
203
+ end
204
+ end
205
+ end
206
+ end