nanoc-core 4.11.1
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.
- checksums.yaml +7 -0
- data/NEWS.md +3 -0
- data/README.md +3 -0
- data/lib/nanoc/core/binary_content.rb +12 -0
- data/lib/nanoc/core/checksummer.rb +287 -0
- data/lib/nanoc/core/code_snippet.rb +57 -0
- data/lib/nanoc/core/configuration-schema.json +122 -0
- data/lib/nanoc/core/configuration.rb +206 -0
- data/lib/nanoc/core/content.rb +46 -0
- data/lib/nanoc/core/context.rb +70 -0
- data/lib/nanoc/core/contracts_support.rb +131 -0
- data/lib/nanoc/core/core_ext/array.rb +54 -0
- data/lib/nanoc/core/core_ext/hash.rb +58 -0
- data/lib/nanoc/core/core_ext/string.rb +20 -0
- data/lib/nanoc/core/data_source.rb +170 -0
- data/lib/nanoc/core/directed_graph.rb +195 -0
- data/lib/nanoc/core/document.rb +124 -0
- data/lib/nanoc/core/error.rb +9 -0
- data/lib/nanoc/core/identifiable_collection.rb +142 -0
- data/lib/nanoc/core/identifier.rb +218 -0
- data/lib/nanoc/core/item.rb +11 -0
- data/lib/nanoc/core/item_collection.rb +15 -0
- data/lib/nanoc/core/item_rep.rb +92 -0
- data/lib/nanoc/core/layout.rb +11 -0
- data/lib/nanoc/core/layout_collection.rb +15 -0
- data/lib/nanoc/core/lazy_value.rb +38 -0
- data/lib/nanoc/core/notification_center.rb +97 -0
- data/lib/nanoc/core/pattern.rb +37 -0
- data/lib/nanoc/core/processing_action.rb +23 -0
- data/lib/nanoc/core/processing_actions/filter.rb +40 -0
- data/lib/nanoc/core/processing_actions/layout.rb +40 -0
- data/lib/nanoc/core/processing_actions/snapshot.rb +50 -0
- data/lib/nanoc/core/processing_actions.rb +12 -0
- data/lib/nanoc/core/regexp_pattern.rb +28 -0
- data/lib/nanoc/core/snapshot_def.rb +22 -0
- data/lib/nanoc/core/string_pattern.rb +29 -0
- data/lib/nanoc/core/temp_filename_factory.rb +53 -0
- data/lib/nanoc/core/textual_content.rb +41 -0
- data/lib/nanoc/core/version.rb +7 -0
- data/lib/nanoc/core.rb +60 -0
- data/lib/nanoc-core.rb +3 -0
- 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
data/README.md
ADDED
@@ -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
|