Skip to content

Commit

Permalink
Merge pull request #1360 from nanoc/check-schema
Browse files Browse the repository at this point in the history
Validate configuration
  • Loading branch information
denisdefreyne committed Oct 3, 2018
2 parents 45bd40a + 319443a commit da8ff1c
Show file tree
Hide file tree
Showing 13 changed files with 584 additions and 2 deletions.
2 changes: 2 additions & 0 deletions nanoc/lib/nanoc.rb
Expand Up @@ -6,6 +6,8 @@
require 'ddmetrics'
require 'ddplugin'
require 'hamster'
require 'json'
require 'json_schema'
require 'parallel'
require 'ref'
require 'slow_enumerator_tools'
Expand Down
8 changes: 8 additions & 0 deletions nanoc/lib/nanoc/base/core_ext/array.rb
Expand Up @@ -15,6 +15,14 @@ def __nanoc_symbolize_keys_recursively
array
end

def __nanoc_stringify_keys_recursively
array = []
each do |element|
array << (element.respond_to?(:__nanoc_stringify_keys_recursively) ? element.__nanoc_stringify_keys_recursively : element)
end
array
end

# Freezes the contents of the array, as well as all array elements. The
# array elements will be frozen using {#__nanoc_freeze_recursively} if they respond
# to that message, or #freeze if they do not.
Expand Down
10 changes: 10 additions & 0 deletions nanoc/lib/nanoc/base/core_ext/hash.rb
Expand Up @@ -17,6 +17,16 @@ def __nanoc_symbolize_keys_recursively
hash
end

def __nanoc_stringify_keys_recursively
hash = {}
each_pair do |key, value|
new_key = key.is_a?(Symbol) ? key.to_s : key
new_value = value.respond_to?(:__nanoc_stringify_keys_recursively) ? value.__nanoc_stringify_keys_recursively : value
hash[new_key] = new_value
end
hash
end

# Freezes the contents of the hash, as well as all hash values. The hash
# values will be frozen using {#__nanoc_freeze_recursively} if they respond to
# that message, or #freeze if they do not.
Expand Down
116 changes: 116 additions & 0 deletions nanoc/lib/nanoc/base/entities/configuration-schema.json
@@ -0,0 +1,116 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Nanoc configuration schema",
"type": "object",
"properties": {
"text_extensions": {
"type": "array",
"items": {
"type": "string"
}
},
"output_dir": {
"type": "string"
},
"index_filenames": {
"type": "array",
"items": {
"type": "string"
}
},
"enable_output_diff": {
"type": "boolean"
},
"prune": {
"type": "object",
"additionalProperties": false,
"properties": {
"auto_prune": {
"type": "boolean"
},
"exclude": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"commands_dirs": {
"type": "array",
"items": {
"type": "string"
}
},
"lib_dirs": {
"type": "array",
"items": {
"type": "string"
}
},
"data_sources": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"items_root": {
"type": "string"
},
"layouts_root": {
"type": "string"
}
}
}
},
"string_pattern_type": {
"type": "string",
"enum": ["glob", "legacy"]
},
"checks": {
"type": "object",
"properties": {
"internal_links": {
"type": "object",
"additionalProperties": false,
"properties": {
"exclude": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"external_links": {
"type": "object",
"additionalProperties": false,
"properties": {
"exclude": {
"type": "array",
"items": {
"type": "string"
}
},
"exclude_files": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"environments": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "object"
}
}
}
}
}
10 changes: 10 additions & 0 deletions nanoc/lib/nanoc/base/entities/configuration.rb
Expand Up @@ -52,6 +52,8 @@ def initialize(hash: {}, dir:, env_name: nil)
@env_name = env_name
@wrapped = hash.__nanoc_symbolize_keys_recursively
@dir = dir

validate
end

contract C::None => self
Expand Down Expand Up @@ -192,5 +194,13 @@ def merge_recursively(config1, config2)
end
end
end

def validate
dir = File.dirname(__FILE__)
schema_data = JSON.parse(File.read(dir + '/configuration-schema.json'))
schema = JsonSchema.parse!(schema_data)
schema.expand_references!
schema.validate!(@wrapped.__nanoc_stringify_keys_recursively)
end
end
end
11 changes: 10 additions & 1 deletion nanoc/lib/nanoc/cli/error_handler.rb
Expand Up @@ -286,7 +286,7 @@ def write_error_message(stream, error, verbose: false)

error = unwrap_error(error)

message = "#{error.class}: #{error.message}"
message = "#{error.class}: #{message_for_error(error)}"
unless verbose
message = "\e[1m\e[31m" + message + "\e[0m"
end
Expand All @@ -295,6 +295,15 @@ def write_error_message(stream, error, verbose: false)
stream.puts resolution.to_s if resolution
end

def message_for_error(error)
case error
when JsonSchema::AggregateError
"\n" + error.errors.map { |e| " * #{e.pointer}: #{e.message}" }.join("\n")
else
error.message
end
end

def write_item_rep(stream, error, verbose: false)
return unless error.is_a?(Nanoc::Int::Errors::CompilationError)

Expand Down
1 change: 1 addition & 0 deletions nanoc/nanoc.gemspec
Expand Up @@ -25,6 +25,7 @@ Gem::Specification.new do |s|
s.add_runtime_dependency('ddmetrics', '~> 1.0')
s.add_runtime_dependency('ddplugin', '~> 1.0')
s.add_runtime_dependency('hamster', '~> 3.0')
s.add_runtime_dependency('json_schema', '~> 0.19')
s.add_runtime_dependency('parallel', '~> 1.12')
s.add_runtime_dependency('ref', '~> 2.0')
s.add_runtime_dependency('slow_enumerator_tools', '~> 1.0')
Expand Down
8 changes: 8 additions & 0 deletions nanoc/spec/nanoc/base/core_ext/array_spec.rb
Expand Up @@ -8,6 +8,14 @@
end
end

describe 'Array#__nanoc_stringify_keys_recursively' do
it 'should convert keys to strings' do
array_old = [:abc, 'xyz', { 'foo' => 'bar', baz: :qux }]
array_new = [:abc, 'xyz', { 'foo' => 'bar', 'baz' => :qux }]
expect(array_old.__nanoc_stringify_keys_recursively).to eql(array_new)
end
end

describe 'Array#__nanoc_freeze_recursively' do
it 'should prevent first-level elements from being modified' do
array = [:a, %i[b c], :d]
Expand Down
14 changes: 14 additions & 0 deletions nanoc/spec/nanoc/base/core_ext/hash_spec.rb
Expand Up @@ -14,6 +14,20 @@
end
end

describe 'Hash#__nanoc_stringify_keys_recursively' do
it 'should convert keys to strings' do
hash_old = { foo: 'bar' }
hash_new = { 'foo' => 'bar' }
expect(hash_old.__nanoc_stringify_keys_recursively).to eql(hash_new)
end

it 'should not require symbol keys' do
hash_old = { Time.now => 'abc' }
hash_new = hash_old
expect(hash_old.__nanoc_stringify_keys_recursively).to eql(hash_new)
end
end

describe 'Hash#__nanoc_freeze_recursively' do
it 'should prevent first-level elements from being modified' do
hash = { a: { b: :c } }
Expand Down

0 comments on commit da8ff1c

Please sign in to comment.