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
-
.check_keys(target, keys, title) ⇒ Object
Public: checks a hash for expected keys.
-
.check_types(data, schema, file) ⇒ Object
Internal: eventually, validate that the values match expected types.
-
.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
.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_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.
35 36 37 38 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 74 75 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_validator.rb', line 35 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 if value.keys.include? 'one_of' if !one_of?(data[key], value['one_of']) puts " * '#{data[key]}' was not in the list " \ "of expected values in #{file}.".red puts " expected one of the following: #{s[1]['one_of']}\n".red return false else next end else next end 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 " * '#{key}' is not a valid key in #{file}. " \ "Expected #{type} but was #{data[key].class}\n\n" return false end 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
19 20 21 22 23 24 25 |
# File 'lib/jekyll_frontmatter_tests/jekyll_frontmatter_tests_helper.rb', line 19 def required?(key, schema) if schema['config'] !schema['config']['optional'].include? key else true 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 |