Class: FrontmatterTests
- Inherits:
-
Jekyll::Command
- Object
- Jekyll::Command
- FrontmatterTests
- Defined in:
- lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_config.rb,
lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_helper.rb,
lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_loader.rb,
lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_tester.rb,
lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_processor.rb,
lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_validator.rb,
lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_initializer.rb
Class Method Summary collapse
- .basepath ⇒ Object
-
.check_keys(target, keys, title) ⇒ Object
Public: checks a hash for expected keys.
- .check_one_of(data, key, value) ⇒ Object
- .check_rules(data, key, value) ⇒ Object
-
.check_types(data, schema, file) ⇒ Object
Internal: eventually, validate that the values match expected types.
- .follows_rules?(value, rules) ⇒ Boolean
-
.init_with_program(prog) ⇒ Object
Internal: fired when ‘jekyll test` is run.
-
.load_schema(file) ⇒ Object
Public: Load a schema from file.
- .one_of?(data, schema) ⇒ Boolean
-
.process(schema) ⇒ Object
Public: processes a collection against a schema.
- .required?(key, schema) ⇒ Boolean
- .schema_config ⇒ Object
-
.test_collections(collections) ⇒ Object
Public: Tests only specific collection documents.
-
.test_everything ⇒ Object
Public: Tests all collections described by a schema file at ‘deploy/tests/schema`.
-
.test_frontmatter(_args, options) ⇒ Object
Public: Processes options passed throguh the command line, runs the appropriate tests.
-
.test_posts ⇒ Object
Public: tests all documents that are “posts”.
Class Method Details
.basepath ⇒ Object
30 31 32 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_validator.rb', line 30 def basepath File.join(Dir.pwd, 'tests', 'schema') end |
.check_keys(target, keys, title) ⇒ Object
Public: checks a hash for expected keys
target - the hash under test keys - an array of keys the data is expected to have, usually loaded from
a schema file by loadschema()
title - A string representing ‘data`’s name
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_validator.rb', line 12 def check_keys(target, keys, title) keys -= ['config'] unless target.respond_to?('keys') puts "The file #{title} is missing all frontmatter.".red return false end diff = keys - target.keys if diff.empty? return true else puts "\nThe file #{title} is missing the following keys:".red for k in diff puts " * #{k}".red end return false end end |
.check_one_of(data, key, value) ⇒ Object
75 76 77 78 79 80 81 82 83 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_validator.rb', line 75 def check_one_of(data, key, value) if value.keys.include?('one_of') && !one_of?(data[key], value['one_of']) puts " * One of error: One of '#{data[key]}' was not".red puts " in the list of expected values in".red puts " #{File.join(basepath, value['one_of'])}\n".yellow false end end |
.check_rules(data, key, value) ⇒ Object
85 86 87 88 89 90 91 92 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_validator.rb', line 85 def check_rules(data, key, value) if value.keys.include?('rules') && !follows_rules?(data[key], value['rules']) puts " * Rules error: One of '#{data[key]}'".red puts " doesn't follow the rules defined in".red puts " #{basepath}/rules.yml\n".yellow false end end |
.check_types(data, schema, file) ⇒ Object
Internal: eventually, validate that the values match expected types
For example, if we expect the ‘date` key to be in yyyy-mm-dd format, validate that it’s been entered in that format. If we expect authors to be an array, make sure we’re getting an array.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_validator.rb', line 39 def check_types(data, schema, file) return false unless data.respond_to?('keys') schema.each do |s| key = s[0] value = s[1] type = if value.class == Hash value['type'] else value end next unless required?(key, schema) if key == 'config' next elsif value.class == Hash next unless value.keys.include?('one_of') || value.keys.include?('rules') violate_one_of = check_one_of(data, key, value) == false violate_rules = check_rules(data, key, value) == false return false if violate_one_of || violate_rules elsif type == 'Array' && data[key].class == Array next elsif type == 'Boolean' && data[key].is_a?(Boolean) next elsif type == 'String' && data[key].class == String next elsif type == 'Date' next else puts " * invalid value for '#{key}' in #{file}. " \ "Expected #{type} but was #{data[key].class}\n\n" return false end end end |
.follows_rules?(value, rules) ⇒ Boolean
19 20 21 22 23 24 25 26 27 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_helper.rb', line 19 def follows_rules?(value, rules) if rules.include?('no-dash') && rules.include?('lowercase') FrontmatterRules.dashless?(value) && FrontmatterRules.lowercase?(value) elsif rules.include?('no-dash') && !rules.include?('lowercase') FrontmatterRules.dashless?(value) elsif !rules.include?('no-dash') && rules.include?('lowercase') FrontmatterRules.lowercase?(value) end end |
.init_with_program(prog) ⇒ Object
Internal: fired when ‘jekyll test` is run.
When ‘jekyll test` runs, `test_frontmatter` is fired with options and args passed from the command line.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_initializer.rb', line 11 def init_with_program(prog) prog.command(:test) do |c| c.syntax 'test [options]' c.description 'Test your site for frontmatter.' c.option 'posts', '-p', 'Target only posts' c.option 'collections', '-c [COLLECTION]', 'Target a specific collection' c.option 'all', '-a', 'Test all collections (Default)' c.action do |args, | = { 'all' => true } if .empty? test_frontmatter(args, ) end end end |
.load_schema(file) ⇒ Object
Public: Load a schema from file.
file - a string containing a filename
Used throughout to load a specific file. In the future the directories where these schema files are located could be loaded from _config.yml
Returns a hash loaded from the YAML doc or exits 1 if no schema file exists.
15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_loader.rb', line 15 def load_schema(file) # binding.pry schema = File.join(Dir.pwd, schema_config['path'], file) # binding.pry if File.exist?(schema) YAML.load_file(schema) else puts "No schema for #{file}" exit 1 end end |
.one_of?(data, schema) ⇒ Boolean
6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_helper.rb', line 6 def one_of?(data, schema) if schema.instance_of?(Array) && data.instance_of?(Array) (schema & data).count == data.count elsif schema.include? '.yml' schema_list = YAML.load_file(File.join(Dir.pwd, 'tests', 'schema', schema)) (schema_list & data).count == data.count elsif schema.instance_of?(String) && data.instance_of?(Array) false else schema == data end end |
.process(schema) ⇒ Object
Public: processes a collection against a schema
schema - the hash-representation of a schema file
Opens each file in the collection’s expected directory and checks the file’s frontmatter for the expected keys and the expected format of the values.
NOTE - As it iterates through files, subdirectories will be ignored
Returns true or false depending on the success of the check.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_processor.rb', line 17 def process(schema) dir = File.join(schema['config']['path']) passfail = [] Dir.open(dir).each do |f| next if File.directory?(File.join(dir, f)) file = File.open(File.join(dir, f)) next if schema['config']['ignore'].include?(f) data = YAML.load_file(file) passfail.push check_keys(data, schema.keys, f) passfail.push check_types(data, schema, File.join(dir, f)) end passfail.keep_if { |p| p == false } if passfail.empty? return true else puts "There were #{passfail.count} errors".red return false end end |
.required?(key, schema) ⇒ Boolean
29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_helper.rb', line 29 def required?(key, schema) is_required = true is_primary = schema[key] schema['config'] = schema['config'] || { 'optional': [] } is_optional = schema['config']['optional'].include?(key) if is_primary && !is_optional is_required elsif (is_primary && is_optional) || (!is_primary && is_optional) !is_required else raise 'The key provided is not in the schema.' end end |
.schema_config ⇒ Object
5 6 7 8 9 10 11 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_config.rb', line 5 def schema_config config = Jekyll.configuration unless config.key?('frontmatter_tests') config['frontmatter_tests'] = { 'path' => File.join('deploy', 'tests', 'schema') } end config['frontmatter_tests'] end |
.test_collections(collections) ⇒ Object
Public: Tests only specific collection documents
collections - a comma separated string of collection names.
‘collections` is split into an array and each document is loaded and processed against its respective schema.
55 56 57 58 59 60 61 62 63 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_tester.rb', line 55 def test_collections(collections) yepnope = [] for c in collections puts "Testing #{c}".green yepnope.push process(load_schema("_#{c}.yml")) puts "Finished testing #{c}".green end yepnope end |
.test_everything ⇒ Object
Public: Tests all collections described by a schema file at ‘deploy/tests/schema`
67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_tester.rb', line 67 def test_everything schema = Dir.open(schema_config['path']) yepnope = [] schema.each do |s| next unless s.start_with?('_') puts "Testing #{s}".green yepnope.push process(load_schema(s)) puts "Finished testing #{s}".green end yepnope end |
.test_frontmatter(_args, options) ⇒ Object
Public: Processes options passed throguh the command line, runs the appropriate tests.
args - command line arguments (example: jekyll test [ARG]) options - command line options (example: jekyll test -[option] [value])
Depending on the flag passed (see ‘init_with_program`), runs the expected # test.
Example: the following comamnd ‘jekyll test -p` will pass “=>
true` as `options`. This will cause `test_frontmatter` to
compare all docs in _posts with the provided schema.
The test runner pushes the result of each test into a ‘results` array and # exits `1` if any tests fail or `0` if all is well.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_tester.rb', line 19 def test_frontmatter(_args, ) puts 'starting tests' if ['posts'] results = test_posts elsif ['collections'] collections = ['collections'].split(',') results = test_collections(collections) else results = test_everything end if results.find_index { |r| r == false } puts 'The test exited with errors, see above.' exit 1 else puts 'Tests finished!' exit 0 end end |
.test_posts ⇒ Object
Public: tests all documents that are “posts”
Loads a schema called _posts.yml and processes all post documents against it.
42 43 44 45 46 47 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_tester.rb', line 42 def test_posts puts 'testing posts'.green yepnope = [].push process(load_schema('_posts.yml')) puts 'Finished testing'.green yepnope end |