Purpose
The modspec
Ruby library allows you to work with OGC ModSpec instances
and export/load them from/to YAML.
OGC ModSpec is a specification for specifying requirements and conformance tests for standards, described in OGC 08-131r3.
Features
This library allows you to:
-
Create and manipulate ModSpec instances:
-
normative statements
-
conformance tests
-
normative statements classes
-
conformance classes
-
unified ModSpec suite
-
-
Load and save objects from/to YAML
-
Perform validations on ModSpec instances
-
Combine ModSpec suites
Installation
Add this line to your application’s Gemfile:
gem 'modspec'
Then execute:
$ bundle install
Or install it yourself:
$ gem install modspec
Basics
ModSpec instances all have the following core attributes:
identifier
-
A unique identifier for the instance, as a URI.
-
The pattern for a ModSpec class is
/<type>/<class>
, where-
<type>
is the type of the instance (req
for normative statement,conf
for conformance test) -
<class>
is the name of the class (normative statements class or conformance class)
-
-
The pattern for a normative statement or conformance test is
/<type>/<class>/<name>
, where-
<type>
is the type of the instance (req
for normative statement,conf
for conformance test) -
<class>
is the name of the class (normative statements class or conformance class) -
<name>
is the name of the instance
-
-
name
-
A human-readable name for the instance
description
-
A description of the instance
guidance
-
Guidance on how to implement the instance
Usage
Normative statements class
A normative statements class groups related normative statements.
A normative statements class has the following attributes in addition to the core attributes:
subject
-
The subject of the normative statements class
dependencies
-
URI(s) of normative statement classes that this class depends on
normative_statements
-
A list of normative statements that belong to this class
belongs_to
-
The normative statements class that this class belongs to
reference
-
A reference to an external document or resource
source
-
The source of the normative statements class
normative_statements_class = Modspec::NormativeStatementsClass.new(
identifier: "/req/example",
name: "Requirements class",
dependencies: ["/req/baf"],
normative_statements: [normative_statement]
)
normative_statements_class.to_yaml
identifier: "/req/example"
name: "Requirements class"
dependencies:
- "/req/baf"
normative_statements:
- identifier: "/req/example/foo"
name: "Example requirement"
statement: "This is an example requirement."
dependencies:
- "/req/bar"
- "/req/baz/2"
> yaml = IO.read('example.yaml')
> normative_statements_class = Modspec::NormativeStatementsClass.from_yaml(yaml)
> <#Modspec::NormativeStatementsClass:0x00007f8b1b8b3d08 @identifier="/req/example", @name="Requirements class", @dependencies=["/req/baf"], @normative_statements=[<#Modspec::NormativeStatement:0x00007f8b1b8b3d08 @identifier="/req/example/foo", @name="Example requirement", @statement="This is an example requirement.", @dependencies=["/req/bar", "/req/baz/2"]>]>
> normative_statements_class.name
> "Requirements class"
Validations:
-
Identifier prefix: All normative statements within a normative statements class must share the same identifier prefix as the class itself, followed by a slash and then the statement’s own identifier.
Normative statement
A normative statement represents a single requirement in the specification.
A normative statement has the following attributes in addition to the core attributes:
subject
-
The subject of the normative statement
obligation
-
The obligation level of the class, one of
requirement
,recommendation
,permission
. Defaults torequirement
. statement
-
The text of the normative statement
condition
-
Conditions that must be met for the statement to apply
inherit
-
URI(s) of normative statement(s) that this statement inherits from
indirect_dependency
-
URI(s) of normative statement(s) that this statement indirectly depends on
implements
-
The higher-level normative statement that this statement implements
dependencies
-
A list of identifiers for other normative statements that this statement depends on.
belongs_to
-
The normative statements class that this statement belongs to.
reference
-
A reference to an external document or resource
parts
-
A list of normative statements classes that this class is composed
normative_statement = Modspec::NormativeStatement.new(
identifier: "/req/example/foo",
name: "Example requirement",
statement: "This is an example requirement statement.",
obligation: "requirement", # default
parts: [
"This is a part of the requirement.",
"This is another part of the requirement."
]
dependencies: ["/req/bar", "/req/baz/2"]
)
normative_statement.to_yaml
Will give out:
identifier: /req/example/foo
name: Example requirement
statement: This is an example requirement statement.
dependencies:
- /req/bar
- /req/baz/2
And to load a normative statement from a YAML file:
> yaml = IO.read('example-foo.yaml')
> normative_statement = Modspec::NormativeStatement.from_yaml(yaml)
> <#Modspec::NormativeStatement:0x00007f8b1b8b3d08 @identifier="/req/example/foo", @name="Example requirement", @statement="This is an example requirement statement.", @dependencies=["/req/bar", "/req/baz/2"]>
> normative_statement.name
> "Example requirement"
Validations:
-
Unique identifier: Each normative statement must have a unique identifier within its parent normative statements class.
-
Valid dependencies: The dependencies listed for a normative statement must refer to valid identifiers of other normative statements.
Conformance class
A conformance class groups related conformance tests.
A conformance class has the following attributes in addition to the core attributes:
tests
-
A set of conformance tests that belong to this class
classification
-
A classification of the conformance class
dependencies
-
A list of identifiers for other conformance classes that this class depends on
target
-
A list of identifiers of normative statement(s) that this class targets
belongs_to
-
A conformance class that this class belongs to
reference
-
A reference to an external document or resource
conformance_class = Modspec::ConformanceClass.new(
identifier: "/conf/example",
name: "Conformance class",
tests: [conformance_test]
)
conformance_class.to_yaml
identifier: "/conf/example"
name: "Conformance class"
tests:
- identifier: "/conf/example/foo"
name: "Example test"
description: "This is an example conformance test."
targets:
- "/req/example/foo"
> yaml = IO.read('example.yaml')
> conformance_class = Modspec::ConformanceClass.from_yaml(yaml)
> <#Modspec::ConformanceClass:0x00007f8b1b8b3d08 @identifier="/conf/example", @name="Conformance class", @tests=[<#Modspec::ConformanceTest:0x00007f8b1b8b3d08 @identifier="/conf/example/foo", @name="Example test", @description="This is an example conformance test.", @targets=["/req/example/foo"]>], @abstract=false>
> conformance_class.name
> "Conformance class"
Validations:
-
Identifier prefix: All conformance tests within a conformance class must share the same identifier prefix as the class itself, followed by a slash and then the test’s own identifier.
-
Valid targets: The targets listed for a conformance test must refer to valid identifiers of existing normative statements.
Conformance test
A conformance test verifies compliance with one or more normative statements.
A conformance test has the following attributes in addition to the core attributes:
targets
-
A list of identifiers for normative statements that this test targets
belongs_to
-
The conformance class that this test belongs to
guidance
-
Guidance on how to perform the test
purpose
-
The purpose of the test
method
-
The method used to perform the test
type
-
The type of the test
reference
-
A reference to an external document or resource
abstract
-
A boolean indicating whether this test is abstract
conformance_test = Modspec::ConformanceTest.new(
identifier: "/conf/example/foo",
name: "Example test",
description: "This is an example conformance test.",
targets: ["/req/example/foo"],
test_method: "manual",
abstract: false
)
conformance_test.to_yaml
identifier: "/conf/example/foo"
name: "Example test"
description: "This is an example conformance test."
abstract: false
test_method: "manual"
targets:
- "/req/example/foo"
> yaml = IO.read('example.yaml')
> conformance_test = Modspec::ConformanceTest.from_yaml(yaml)
> <#Modspec::ConformanceTest:0x00007f8b1b8b3d08 @identifier="/conf/example/foo", @name="Example test", @description="This is an example conformance test.", @targets=["/req/example/foo"], @test_method="manual", @abstract=false>
> conformance_test.name
> "Example test"
Validations:
-
Valid targets: The targets listed for a conformance test must refer to valid identifiers of existing normative statements.
Suite
A suite represents the entire specification, including all normative statements and conformance tests.
Note
|
This is not defined in the ModSpec specification. |
identifier: "example-suite"
name: "Example suite"
normative_statements_classes:
- identifier: "/req/example"
name: "Requirements class"
normative_statements:
- identifier: "/req/example/foo"
name: "Example requirement"
statement: "This is an example requirement statement."
dependencies:
- "/req/bar"
- "/req/baz/2"
conformance_classes:
- identifier: "/conf/example"
name: "Conformance class"
tests:
- identifier: "/conf/example/foo"
name: "Example test"
description: "This is an example conformance test."
targets:
- "/req/example/foo"
> yaml = IO.read('example-suite.yaml')
> suite = Modspec::Suite.from_yaml(yaml)
> <#Modspec::Suite:0x00007f8b1b8b3d08 @identifier="example-suite", @name="Example suite", @normative_statements_classes=[<#Modspec::NormativeStatementsClass:0x00007f8b1b8b3d08 @identifier="/req/example", @name="Requirements class", @dependencies=["/req/baf"], @normative_statements=[<#Modspec::NormativeStatement:0x00007f8b1b8b3d08 @identifier="/req/example/foo", @name="Example requirement", @statement="This is an example requirement statement.", @dependencies=["/req/bar", "/req/baz/2"]>]>], @conformance_classes=[<#Modspec::ConformanceClass:0x00007f8b1b8b3d08 @identifier="/conf/example", @name="Conformance class", @tests=[<#Modspec::ConformanceTest:0x00007f8b1b8b3d08 @identifier="/conf/example/foo", @name="Example test", @description="This is an example conformance test.", @targets=["/req/example/foo"]>], @abstract=false>]>
> suite.normative_statements_classes.first.name
> "Requirements class"
Validations:
-
No cyclic dependencies: The suite ensures that there are no circular dependencies among normative statements.
-
Label uniqueness: Labels must be unique across all classes within the suite.
-
Dependency validation: The suite verifies that all dependencies between normative statements and conformance tests are valid and refer to existing elements.
The Suite class provides a from_yaml_files
method to load a Suite instance
from multiple YAML files. This is particularly useful when your specification is
split across several files for better organization and maintainability.
To load a Suite from multiple YAML files:
require 'modspec'
# Specify the paths to your YAML files
yaml_files = [
'path/to/normative_statements.yaml',
'path/to/conformance_tests.yaml'
]
# Create a Suite instance from the YAML files
suite = Modspec::Suite.from_yaml_files(yaml_files)
# Now you can work with the suite object
puts suite.name
puts suite.normative_statements_classes.count
puts suite.conformance_classes.count
The Suite
class provides a combine
method to merge two suites:
combined_suite = suite1.combine(suite2)
This method merges the two suites into a single suite, ensuring that the resulting suite is consistent and free of conflicts.
Validation process
Validations are typically performed when:
-
Creating or modifying individual elements (normative statements, conformance tests, etc.)
-
Adding elements to their respective classes
-
Combining suites
-
Loading a suite from YAML or other formats
If any validation fails, an error or a collection of errors is returned, describing the specific validation issues encountered.
To manually trigger validation on a suite:
errors = suite.validate_all
if errors.any?
puts "Validation errors:"
errors.each { |error| puts "- #{error}" }
else
puts "Suite is valid."
end
These validation mechanisms help ensure that the created ModSpec instances are consistent, well-formed, and adhere to the expected structure and relationships.
Copyright
This gem is developed, maintained and funded by Ribose Inc.
License
The gem is available as open source under the terms of the 2-Clause BSD License.