diff --git a/lib/cff/file.rb b/lib/cff/file.rb index da4d386..078c4f3 100644 --- a/lib/cff/file.rb +++ b/lib/cff/file.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) 2018-2022 The Ruby Citation File Format Developers. +# Copyright (c) 2018-2024 The Ruby Citation File Format Developers. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -108,7 +108,7 @@ def self.open(file) end # :call-seq: - # validate(filename, fail_on_filename: true) -> Array + # validate(filename, validate_as: nil, fail_on_filename: true) -> Array # # Read a file and return an array with the result. The result array is a # three-element array, with `true`/`false` at index 0 to indicate @@ -116,23 +116,33 @@ def self.open(file) # `true`/`false` at index 2 to indicate whether the filename passed/failed # validation. # + # Setting `validate_as` to a specific version will validate against that + # version of the schema, rather than the version specified in the CFF file. + # If the version specified is not a valid schema version, the version in + # the CFF file, or the default schema version will be used. + # # You can choose whether filename validation failure should cause overall # validation failure with the `fail_on_filename` parameter (default: true). - def self.validate(file, fail_on_filename: true) - File.read(file).validate(fail_on_filename: fail_on_filename) + def self.validate(file, validate_as: nil, fail_on_filename: true) + File.read(file).validate(validate_as: validate_as, fail_on_filename: fail_on_filename) end # :call-seq: - # validate!(filename, fail_on_filename: true) + # validate!(filename, validate_as: nil, fail_on_filename: true) # # Read a file and raise a ValidationError upon failure. If an error is # raised it will contain the detected validation failures for further # inspection. # + # Setting `validate_as` to a specific version will validate against that + # version of the schema, rather than the version specified in the CFF file. + # If the version specified is not a valid schema version, the version in + # the CFF file, or the default schema version will be used. + # # You can choose whether filename validation failure should cause overall # validation failure with the `fail_on_filename` parameter (default: true). - def self.validate!(file, fail_on_filename: true) - File.read(file).validate!(fail_on_filename: fail_on_filename) + def self.validate!(file, validate_as: nil, fail_on_filename: true) + File.read(file).validate!(validate_as: validate_as, fail_on_filename: fail_on_filename) end # :call-seq: @@ -158,11 +168,19 @@ def self.write(file, cff, comment = '') # `true`/`false` at index 2 to indicate whether the filename passed/failed # validation. # + # Setting `fail_fast` to true will fail validation at the first detected + # failure, rather than gathering and returning all failures. + # + # Setting `validate_as` to a specific version will validate against that + # version of the schema, rather than the version specified in the CFF file. + # If the version specified is not a valid schema version, the version in + # the CFF file, or the default schema version will be used. + # # You can choose whether filename validation failure should cause overall # validation failure with the `fail_on_filename` parameter (default: true). - def validate(fail_fast: false, fail_on_filename: true) + def validate(fail_fast: false, validate_as: nil, fail_on_filename: true) valid_filename = (::File.basename(@filename) == CFF_VALID_FILENAME) - result = (@index.validate(fail_fast: fail_fast) << valid_filename) + result = (@index.validate(fail_fast: fail_fast, validate_as: validate_as) << valid_filename) result[0] &&= valid_filename if fail_on_filename result @@ -175,11 +193,19 @@ def validate(fail_fast: false, fail_on_filename: true) # is raised it will contain the detected validation failures for further # inspection. # + # Setting `fail_fast` to true will fail validation at the first detected + # failure, rather than gathering and returning all failures. + # + # Setting `validate_as` to a specific version will validate against that + # version of the schema, rather than the version specified in the CFF file. + # If the version specified is not a valid schema version, the version in + # the CFF file, or the default schema version will be used. + # # You can choose whether filename validation failure should cause overall # validation failure with the `fail_on_filename` parameter (default: true). - def validate!(fail_fast: false, fail_on_filename: true) + def validate!(fail_fast: false, validate_as: nil, fail_on_filename: true) result = validate( - fail_fast: fail_fast, fail_on_filename: fail_on_filename + fail_fast: fail_fast, validate_as: validate_as, fail_on_filename: fail_on_filename ) return if result[0] diff --git a/lib/cff/validatable.rb b/lib/cff/validatable.rb index 10094e7..c0b6d76 100644 --- a/lib/cff/validatable.rb +++ b/lib/cff/validatable.rb @@ -31,33 +31,48 @@ module Validatable end.freeze # :nodoc: # :call-seq: - # validate!(fail_fast: false) + # validate!(fail_fast: false, validate_as: nil) # # Validate a CFF file or model (Index) and raise a ValidationError upon # failure. If an error is raised it will contain the detected validation - # failures for further inspection. Setting `fail_fast` to true will fail - # validation at the first detected failure, rather than gathering and - # returning all failures. - def validate!(fail_fast: false) - result = validate(fail_fast: fail_fast) + # failures for further inspection. + # + # Setting `fail_fast` to true will fail validation at the first detected + # failure, rather than gathering and returning all failures. + # + # Setting `validate_as` to a specific version will validate against that + # version of the schema, rather than the version specified in the CFF file. + # If the version specified is not a valid schema version, the version in + # the CFF file, or the default schema version will be used. + def validate!(fail_fast: false, validate_as: nil) + result = validate(fail_fast: fail_fast, validate_as: validate_as) return if result[0] raise ValidationError.new(result[1]) end # :call-seq: - # validate(fail_fast: false) -> Array + # validate(fail_fast: false, validate_as: nil) -> Array # # Validate a CFF file or model (Index) and return an array with the result. # The result array is a two-element array, with `true`/`false` at index 0 # to indicate pass/fail, and an array of errors at index 1 (if any). + # # Setting `fail_fast` to true will fail validation at the first detected # failure, rather than gathering and returning all failures. - def validate(fail_fast: false) - schema = @fields['cff-version'] + # + # Setting `validate_as` to a specific version will validate against that + # version of the schema, rather than the version specified in the CFF file. + # If the version specified is not a valid schema version, the version in + # the CFF file, or the default schema version will be used. + def validate(fail_fast: false, validate_as: nil) + schema = Schemas::VERSIONS.include?(validate_as) ? validate_as : @fields['cff-version'] schema = Schemas::DEFAULT_VERSION if schema.nil? || schema.empty? - SCHEMAS[schema].validate(fields(validate: true), fail_fast: fail_fast) + model = fields(validate: true) + model['cff-version'] = schema unless validate_as.nil? + + SCHEMAS[schema].validate(model, fail_fast: fail_fast) end end end diff --git a/test/validatable_test.rb b/test/validatable_test.rb index 0820ae0..133b91a 100644 --- a/test/validatable_test.rb +++ b/test/validatable_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) 2018-2022 The Ruby Citation File Format Developers. +# Copyright (c) 2018-2024 The Ruby Citation File Format Developers. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -180,4 +180,30 @@ def test_valid_filename_validates assert_empty(result[1]) assert(result[2]) end + + def test_validate_model_against_specific_schema_version + cff = ::CFF::Index.read(File.read(SHORT_CFF)) + + # Validate a 1.2.0 CFF against schema version 1.3.0. + result = cff.validate(validate_as: '1.3.0') + assert(result[0]) + assert_empty(result[1]) + assert_equal('1.2.0', cff.cff_version) # No change to the model. + + cff.validate!(validate_as: '1.3.0') + assert_equal('1.2.0', cff.cff_version) # No change to the model. + end + + def test_validate_file_against_specific_schema_version + cff = ::CFF::File.read(SHORT_CFF) + + # Validate a 1.2.0 CFF against schema version 1.3.0. + result = cff.validate(validate_as: '1.3.0', fail_on_filename: false) + assert(result[0]) + assert_empty(result[1]) + assert_equal('1.2.0', cff.cff_version) # No change to the model. + + cff.validate!(validate_as: '1.3.0', fail_on_filename: false) + assert_equal('1.2.0', cff.cff_version) # No change to the model. + end end