diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 0000000..5252434
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,24 @@
+# This configuration was generated by
+# `rubocop --auto-gen-config`
+# on 2024-11-01 07:12:25 UTC using RuboCop version 1.68.0.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
+# URISchemes: http, https
+Layout/LineLength:
+ Exclude:
+ - 'spec/oasis/etm/entry_spec.rb'
+
+# Offense count: 6
+# Configuration parameters: CountAsOne.
+RSpec/ExampleLength:
+ Max: 10
+
+# Offense count: 12
+RSpec/MultipleExpectations:
+ Max: 10
diff --git a/Gemfile b/Gemfile
index ffb335f..02c2b1e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,8 +5,11 @@ source "https://rubygems.org"
# Specify your gem's dependencies in oasis-etm.gemspec
gemspec
+gem "nokogiri"
gem "rake", "~> 13.0"
-
gem "rspec", "~> 3.0"
-
gem "rubocop", "~> 1.21"
+gem "rubocop-performance"
+gem "rubocop-rake"
+gem "rubocop-rspec"
+gem "xml-c14n"
diff --git a/README.adoc b/README.adoc
index be84279..c81542c 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1,42 +1,4 @@
-= OASIS Exchange Table Model library
-
-https://github.com/lutaml/oasis-etm[image:https://img.shields.io/github/stars/lutaml/oasis-etm.svg?style=social[GitHub Stars]]
-https://github.com/lutaml/oasis-etm[image:https://img.shields.io/github/forks/lutaml/oasis-etm.svg?style=social[GitHub Forks]]
-image:https://img.shields.io/github/license/lutaml/oasis-etm.svg[License]
-image:https://img.shields.io/github/actions/workflow/status/lutaml/oasis-etm/test.yml?branch=main[Build Status]
-image:https://img.shields.io/gem/v/oasis-etm.svg[RubyGems Version]
-
-== Purpose
-
-
-== Features
-
-
-== Installation
-
-Add this line to your application's Gemfile:
-
-[source,ruby]
-----
-gem 'oasis-etm'
-----
-
-And then execute:
-
-[source,shell]
-----
-bundle install
-----
-
-Or install it yourself as:
-
-[source,shell]
-----
-gem install oasis-etm
-----
-
= OASIS Exchange Table Model (ETM) Parser
-:source-highlighter: rouge
:toc: macro
:toclevels: 3
@@ -48,8 +10,26 @@ toc::[]
== Purpose
-Ruby library to parse and create OASIS Exchange Table Model (ETM) formatted tables.
-This library provides a Ruby implementation of the OASIS Technical Resolution TR 9503:1995.
+The OASIS ETM format is a simple XML format for representing tables for
+exchange. It is used in the DocBook and NISO JATS standards.
+
+This library provides a Ruby implementation of the
+https://www.oasis-open.org/specs/a503.htm[OASIS Technical Resolution TR 9503:1995].
+
+== Features
+
+* Full implementation of the OASIS Exchange Table Model TR 9503:1995
+* Intentionally excludes CALS table features not part of the Exchange subset (like `tfoot`)
+* XML serialization and deserialization
+* Validation of attribute values
+* Support for all Exchange Table Model elements:
+** `table`
+** `tgroup`
+** `colspec`
+** `thead`
+** `tbody`
+** `row`
+** `entry`
== Installation
@@ -62,22 +42,22 @@ gem 'oasis-etm'
And then execute:
-[source,sh]
+[source,shell]
----
-$ bundle install
+bundle install
----
Or install it yourself as:
-[source,sh]
+[source,shell]
----
-$ gem install oasis-etm
+gem install oasis-etm
----
== Usage
-=== Basic Example
+=== Basic example
[source,ruby]
----
@@ -119,7 +99,7 @@ table = Oasis::Etm::Table.new(
xml = table.to_xml
----
-=== XML Schema
+=== XML schema
The OASIS ETM format follows this basic structure:
@@ -149,6 +129,24 @@ The OASIS ETM format follows this basic structure:
----
+== Development
+
+=== Test files
+
+`spec/fixtures/native/docbook_example.xml`::
+https://tdg.docbook.org/tdg/4.5/table
+
+`spec/fixtures/niso-jats/niso-jats-table-wrap.xml`::
+https://jats.nlm.nih.gov/options/OASIS/tag-library/19990315/element/oasis-table.html
+This is to test the OASIS exchange table model when it is namespaced from
+another location.
+
+`spec/fixtures/isosts/isosts_tables.cals.{nn}.xml`::
+Tables extracted from https://www.iso.org/schema/isosts/cals/test/isosts_tables.cals.xml.
+This is to test the OASIS exchange table model when it is namespaced from
+another location.
+
+
== Contributing
Bug reports and pull requests are welcome. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
diff --git a/lib/oasis/etm/entry.rb b/lib/oasis/etm/entry.rb
index 83cd9af..a527614 100644
--- a/lib/oasis/etm/entry.rb
+++ b/lib/oasis/etm/entry.rb
@@ -14,7 +14,7 @@ class Entry < Lutaml::Model::Serializable
attribute :valign, :string, values: %w[top middle bottom]
# Content
- attribute :content, :string
+ attribute :content, :string, raw: true
xml do
root "entry"
diff --git a/lib/oasis/etm/table.rb b/lib/oasis/etm/table.rb
index 5bdcb2b..9ee177b 100644
--- a/lib/oasis/etm/table.rb
+++ b/lib/oasis/etm/table.rb
@@ -14,7 +14,7 @@ class Table < Lutaml::Model::Serializable
attribute :tgroups, Tgroup, collection: true
xml do
- root "table"
+ root "table", ordered: true
# Frame mappings
map_attribute "frame", to: :frame
diff --git a/lib/oasis/etm/tgroup.rb b/lib/oasis/etm/tgroup.rb
index 1893560..d9a7cde 100644
--- a/lib/oasis/etm/tgroup.rb
+++ b/lib/oasis/etm/tgroup.rb
@@ -19,7 +19,7 @@ class Tgroup < Lutaml::Model::Serializable
attribute :tbody, Tbody
xml do
- root "tgroup"
+ root "tgroup", ordered: true
# Attribute mappings
map_attribute "cols", to: :cols
diff --git a/oasis-etm_wrapped.txt b/oasis-etm_wrapped.txt
deleted file mode 100644
index cc7a7a2..0000000
--- a/oasis-etm_wrapped.txt
+++ /dev/null
@@ -1,434 +0,0 @@
---- START FILE: CODE_OF_CONDUCT.md ---
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-We as members, contributors, and leaders pledge to make participation in our
-community a harassment-free experience for everyone, regardless of age, body
-size, visible or invisible disability, ethnicity, sex characteristics, gender
-identity and expression, level of experience, education, socio-economic status,
-nationality, personal appearance, race, caste, color, religion, or sexual
-identity and orientation.
-
-We pledge to act and interact in ways that contribute to an open, welcoming,
-diverse, inclusive, and healthy community.
-
-## Our Standards
-
-Examples of behavior that contributes to a positive environment for our
-community include:
-
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes,
- and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the overall
- community
-
-Examples of unacceptable behavior include:
-
-* The use of sexualized language or imagery, and sexual attention or advances of
- any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or email address,
- without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
- professional setting
-
-## Enforcement Responsibilities
-
-Community leaders are responsible for clarifying and enforcing our standards of
-acceptable behavior and will take appropriate and fair corrective action in
-response to any behavior that they deem inappropriate, threatening, offensive,
-or harmful.
-
-Community leaders have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are
-not aligned to this Code of Conduct, and will communicate reasons for moderation
-decisions when appropriate.
-
-## Scope
-
-This Code of Conduct applies within all community spaces, and also applies when
-an individual is officially representing the community in public spaces.
-Examples of representing our community include using an official email address,
-posting via an official social media account, or acting as an appointed
-representative at an online or offline event.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported to the community leaders responsible for enforcement at
-[INSERT CONTACT METHOD].
-All complaints will be reviewed and investigated promptly and fairly.
-
-All community leaders are obligated to respect the privacy and security of the
-reporter of any incident.
-
-## Enforcement Guidelines
-
-Community leaders will follow these Community Impact Guidelines in determining
-the consequences for any action they deem in violation of this Code of Conduct:
-
-### 1. Correction
-
-**Community Impact**: Use of inappropriate language or other behavior deemed
-unprofessional or unwelcome in the community.
-
-**Consequence**: A private, written warning from community leaders, providing
-clarity around the nature of the violation and an explanation of why the
-behavior was inappropriate. A public apology may be requested.
-
-### 2. Warning
-
-**Community Impact**: A violation through a single incident or series of
-actions.
-
-**Consequence**: A warning with consequences for continued behavior. No
-interaction with the people involved, including unsolicited interaction with
-those enforcing the Code of Conduct, for a specified period of time. This
-includes avoiding interactions in community spaces as well as external channels
-like social media. Violating these terms may lead to a temporary or permanent
-ban.
-
-### 3. Temporary Ban
-
-**Community Impact**: A serious violation of community standards, including
-sustained inappropriate behavior.
-
-**Consequence**: A temporary ban from any sort of interaction or public
-communication with the community for a specified period of time. No public or
-private interaction with the people involved, including unsolicited interaction
-with those enforcing the Code of Conduct, is allowed during this period.
-Violating these terms may lead to a permanent ban.
-
-### 4. Permanent Ban
-
-**Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior, harassment of an
-individual, or aggression toward or disparagement of classes of individuals.
-
-**Consequence**: A permanent ban from any sort of public interaction within the
-community.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage],
-version 2.1, available at
-[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
-
-Community Impact Guidelines were inspired by
-[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
-
-For answers to common questions about this code of conduct, see the FAQ at
-[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
-[https://www.contributor-covenant.org/translations][translations].
-
-[homepage]: https://www.contributor-covenant.org
-[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
-[Mozilla CoC]: https://github.com/mozilla/diversity
-[FAQ]: https://www.contributor-covenant.org/faq
-[translations]: https://www.contributor-covenant.org/translations
---- END FILE: CODE_OF_CONDUCT.md ---
---- START FILE: Gemfile ---
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-# Specify your gem's dependencies in oasis-etm.gemspec
-gemspec
-
-gem "rake", "~> 13.0"
-
-gem "rspec", "~> 3.0"
-
-gem "rubocop", "~> 1.21"
---- END FILE: Gemfile ---
---- START FILE: README.adoc ---
-= OASIS Exchange Table Model library
-
-https://github.com/lutaml/oasis-etm[image:https://img.shields.io/github/stars/lutaml/oasis-etm.svg?style=social[GitHub Stars]]
-https://github.com/lutaml/oasis-etm[image:https://img.shields.io/github/forks/lutaml/oasis-etm.svg?style=social[GitHub Forks]]
-image:https://img.shields.io/github/license/lutaml/oasis-etm.svg[License]
-image:https://img.shields.io/github/actions/workflow/status/lutaml/oasis-etm/test.yml?branch=main[Build Status]
-image:https://img.shields.io/gem/v/oasis-etm.svg[RubyGems Version]
-
-== Purpose
-
-
-== Features
-
-
-== Installation
-
-Add this line to your application's Gemfile:
-
-[source,ruby]
-----
-gem 'oasis-etm'
-----
-
-And then execute:
-
-[source,shell]
-----
-bundle install
-----
-
-Or install it yourself as:
-
-[source,shell]
-----
-gem install oasis-etm
-----
-
-= OASIS Exchange Table Model (ETM) Parser
-:source-highlighter: rouge
-:toc: macro
-:toclevels: 3
-
-image:https://img.shields.io/gem/v/oasis-etm.svg[Gem Version, link=https://rubygems.org/gems/oasis-etm]
-image:https://github.com/yourusername/oasis-etm/actions/workflows/test.yml/badge.svg[Build Status, link=https://github.com/yourusername/oasis-etm/actions/workflows/test.yml]
-image:https://img.shields.io/github/license/yourusername/oasis-etm.svg[License, link=https://github.com/yourusername/oasis-etm/blob/main/LICENSE]
-
-toc::[]
-
-== Purpose
-
-Ruby library to parse and create OASIS Exchange Table Model (ETM) formatted tables.
-This library provides a Ruby implementation of the OASIS Technical Resolution TR 9503:1995.
-
-== Installation
-
-Add this line to your application's Gemfile:
-
-[source,ruby]
-----
-gem 'oasis-etm'
-----
-
-And then execute:
-
-[source,sh]
-----
-$ bundle install
-----
-
-Or install it yourself as:
-
-[source,sh]
-----
-$ gem install oasis-etm
-----
-
-
-== Usage
-
-=== Basic Example
-
-[source,ruby]
-----
-require 'oasis-etm'
-
-# Parse an ETM XML file
-table = Oasis::Etm::Table.from_xml(File.read('table.xml'))
-
-# Access table attributes
-puts table.frame
-puts table.colsep
-puts table.rowsep
-
-# Access table content
-table.tgroups.each do |tgroup|
- tgroup.colspecs.each do |colspec|
- puts "Column #{colspec.colnum}: #{colspec.colwidth}"
- end
-end
-
-# Create a new table
-table = Oasis::Etm::Table.new(
- frame: 'all',
- colsep: 1,
- rowsep: 1,
- tgroups: [
- Oasis::Etm::Tgroup.new(
- cols: 3,
- colspecs: [
- Oasis::Etm::Colspec.new(colnum: 1, colwidth: '1*'),
- Oasis::Etm::Colspec.new(colnum: 2, colwidth: '2*'),
- Oasis::Etm::Colspec.new(colnum: 3, colwidth: '1*')
- ]
- )
- ]
-)
-
-# Convert to XML
-xml = table.to_xml
-----
-
-=== XML Schema
-
-The OASIS ETM format follows this basic structure:
-
-[source,xml]
-----
-
- Sample Table
-
-
-
-
-
-
- Header 1
- Header 2
- Header 3
-
-
-
-
- Cell 1
- Cell 2
- Cell 3
-
-
-
-
-----
-
-== Contributing
-
-Bug reports and pull requests are welcome. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
-
-. Fork it
-. Create your feature branch (`git checkout -b my-new-feature`)
-. Commit your changes (`git commit -am 'Add some feature'`)
-. Push to the branch (`git push origin my-new-feature`)
-. Create new Pull Request
-
-
-== License and Copyright
-
-This project is licensed under the BSD 2-clause License.
-See the link:LICENSE.md[] file for details.
-
-Copyright Ribose.
---- END FILE: README.adoc ---
---- START FILE: Rakefile ---
-# frozen_string_literal: true
-
-require "bundler/gem_tasks"
-require "rspec/core/rake_task"
-
-RSpec::Core::RakeTask.new(:spec)
-
-require "rubocop/rake_task"
-
-RuboCop::RakeTask.new
-
-task default: %i[spec rubocop]
---- END FILE: Rakefile ---
---- START FILE: lib/oasis/etm.rb ---
-# frozen_string_literal: true
-
-require "lutaml/model"
-require_relative "etm/version"
-require_relative "etm/table"
-
-module Oasis
- module Etm
- class Error < StandardError; end
-
- # Your code goes here...
- end
-end
---- END FILE: lib/oasis/etm.rb ---
---- START FILE: lib/oasis/etm/version.rb ---
-# frozen_string_literal: true
-
-module Oasis
- module Etm
- VERSION = "0.1.0"
- end
-end
---- END FILE: lib/oasis/etm/version.rb ---
---- START FILE: oasis-etm.gemspec ---
-# frozen_string_literal: true
-
-require_relative "lib/oasis/etm/version"
-
-Gem::Specification.new do |spec|
- spec.name = "oasis-etm"
- spec.version = Oasis::Etm::VERSION
- spec.authors = ["Ribose Inc."]
- spec.email = ["open.source@ribose.com"]
-
- spec.summary = "Library for OASIS Exchange Table Model"
- spec.description = <<~DESCRIPTION
- Library for manipulation of OASIS Exchange Table Model XML.
- DESCRIPTION
-
- spec.homepage = "https://github.com/lutaml/oasis-etm"
- spec.license = "BSD-2-Clause"
-
- spec.bindir = "exe"
- spec.require_paths = ["lib"]
- spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
-
- # Specify which files should be added to the gem when it is released.
- # The `git ls-files -z` loads the files in the
- # RubyGem that have been added into git.
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
- `git ls-files -z`.split("\x0").reject do |f|
- f.match(%r{^(test|features)/})
- end
- end
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
-
- spec.add_dependency "lutaml-model"
- spec.metadata["rubygems_mfa_required"] = "true"
-end
---- END FILE: oasis-etm.gemspec ---
---- START FILE: sig/oasis/etm.rbs ---
-module Oasis
- module Etm
- VERSION: String
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
- end
-end
---- END FILE: sig/oasis/etm.rbs ---
---- START FILE: spec/oasis/etm_spec.rb ---
-# frozen_string_literal: true
-
-RSpec.describe Oasis::Etm do
- it "has a version number" do
- expect(Oasis::Etm::VERSION).not_to be nil
- end
-
- it "does something useful" do
- expect(false).to eq(true)
- end
-end
---- END FILE: spec/oasis/etm_spec.rb ---
---- START FILE: spec/spec_helper.rb ---
-# spec/spec_helper.rb
-require "bundler/setup"
-require "oasis-etm"
-
-# Require all support files
-Dir[File.join(__dir__, "support", "**", "*.rb")].sort.each { |f| require f }
-
-RSpec.configure do |config|
- # Enable flags like --only-failures and --next-failure
- config.example_status_persistence_file_path = ".rspec_status"
-
- # Disable RSpec exposing methods globally on `Module` and `main`
- config.disable_monkey_patching!
-
- config.expect_with :rspec do |c|
- c.syntax = :expect
- end
-end
---- END FILE: spec/spec_helper.rb ---
diff --git a/spec/fixtures/isosts/isosts_tables.cals.01.xml b/spec/fixtures/isosts/isosts_tables.cals.01.xml
new file mode 100644
index 0000000..f3391e0
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.01.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.02.xml b/spec/fixtures/isosts/isosts_tables.cals.02.xml
new file mode 100644
index 0000000..5609c0f
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.02.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+ header text
+
+
+ header text
+
+
+ header text
+
+
+ header text
+
+
+ header text
+
+
+
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.03.xml b/spec/fixtures/isosts/isosts_tables.cals.03.xml
new file mode 100644
index 0000000..bb04afa
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.03.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+ header text
+ header text
+
+
+ header text
+ header text
+ header text
+
+
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.04.xml b/spec/fixtures/isosts/isosts_tables.cals.04.xml
new file mode 100644
index 0000000..dcf85fc
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.04.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+ header text
+ header text
+ header text
+ header text
+
+
+
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table footer
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.05.xml b/spec/fixtures/isosts/isosts_tables.cals.05.xml
new file mode 100644
index 0000000..779d725
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.05.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ header text
+ header text
+ header text
+ header text
+ header text
+
+
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table cell text
+
+
+ table cell text
+ table cell text
+
+
+ table cell text
+
+
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+
+
+ table cell text
+
+
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.06.xml b/spec/fixtures/isosts/isosts_tables.cals.06.xml
new file mode 100644
index 0000000..e92e139
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.06.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+ header text
+ header text
+
+
+ header text
+ header text
+ header text
+
+
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+ table cell text
+ table cell text
+ table cell text
+ table cell text
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.07.xml b/spec/fixtures/isosts/isosts_tables.cals.07.xml
new file mode 100644
index 0000000..6533eb6
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.07.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+ table default
+ table default
+ table default
+ table default
+
+
+
+
+ rowsep=no
+ table default
+ rowsep=yes
+
+
+ colsep=yes, rowsep=yes
+ table default
+
+
+ table default
+ colsep=yes
+ table default
+ table default
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.08.xml b/spec/fixtures/isosts/isosts_tables.cals.08.xml
new file mode 100644
index 0000000..3793162
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.08.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+
+
+
+
+
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+
+
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+
+
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.09.xml b/spec/fixtures/isosts/isosts_tables.cals.09.xml
new file mode 100644
index 0000000..9ed2800
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.09.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ colsep=yes
+ colsep=yes
+ colsep=yes
+ colsep=yes
+
+
+
+
+
+ colsep=yes
+ colsep=yes
+ colsep=yes
+ colsep=yes
+
+
+ colsep=yes
+ colsep=yes
+ colsep=yes
+ colsep=yes
+
+
+ colsep=yes
+ colsep=yes
+ colsep=yes
+ colsep=yes
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.10.xml b/spec/fixtures/isosts/isosts_tables.cals.10.xml
new file mode 100644
index 0000000..680d16e
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.10.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+ table default
+ table default
+ table default
+ table default
+
+
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+
+
+
+
+
+ table default
+ table default
+ table default
+ table default
+
+
+ rowsep=yes; because its the last tbody row before the tfoot
+ rowsep=yes
+ rowsep=yes
+ rowsep=yes
+
+
+ table default
+ table default
+ table default
+ table default
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.11.xml b/spec/fixtures/isosts/isosts_tables.cals.11.xml
new file mode 100644
index 0000000..74fba67
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.11.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+
+
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+
+
+
+
+
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+
+
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+
+
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+ colsep=yes, rowsep=yes
+
+
+
+
diff --git a/spec/fixtures/isosts/isosts_tables.cals.12.xml b/spec/fixtures/isosts/isosts_tables.cals.12.xml
new file mode 100644
index 0000000..776aece
--- /dev/null
+++ b/spec/fixtures/isosts/isosts_tables.cals.12.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+ just lots of text to see the vertical
+ alignment, just lots of text to see the vertical alignment, just lots of text to see the vertical alignment, just lots of text to see the vertical
+ alignment
+ align="center" valign="middle"
+ align="left" valign="top"
+ align="right" valign="bottom"
+ align="char"
+ align="justify" valign="bottom"
+
+
+
+
\ No newline at end of file
diff --git a/spec/fixtures/native/docbook_example.xml b/spec/fixtures/native/docbook_example.xml
new file mode 100644
index 0000000..d39369a
--- /dev/null
+++ b/spec/fixtures/native/docbook_example.xml
@@ -0,0 +1,48 @@
+
+
+Sample Table
+
+
+
+
+
+
+
+ Horizontal Span
+ a3
+ a4
+ a5
+
+
+
+
+
+ b1
+ b2
+ b3
+ b4
+
+ Vertical Span
+
+
+ c1
+ Span Both
+ c4
+
+
+ d1
+ d4
+ d5
+
+
+
+
\ No newline at end of file
diff --git a/spec/fixtures/niso-jats/niso-jats-table-wrap.xml b/spec/fixtures/niso-jats/niso-jats-table-wrap.xml
new file mode 100644
index 0000000..b82b4e4
--- /dev/null
+++ b/spec/fixtures/niso-jats/niso-jats-table-wrap.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+Institutional care
+
+ Bed use (days)
+
+
+
+
+
+
+Control group
+Day hospital
+Control
+Odds ratio (95% CI)
+
+Day hospital
+Control
+
+
+
+
+
+
+
+Comprehensive care (5 trials)
+151/597
+159/584
+0.91 (0.70 to 1.19)
+
+20.5
+21.4
+
+
+Domiciliary care (4 trials)
+20/216
+ 19/227
+1.61 (0.30 to 8.55)
+
+ 7.7
+11.1
+
+
+No comprehensive care (3 trials)
+37/411
+ 66/403
+0.50 (0.26 to 0.96)
+
+11.2
+11.7
+
+
+Total
+208/1224
+244/1214
+0.77 (0.52 to 1.13)
+
+15.0
+16.4
+
+
+
+
diff --git a/spec/oasis/etm/colspec_spec.rb b/spec/oasis/etm/colspec_spec.rb
index 76297ec..c7f1505 100644
--- a/spec/oasis/etm/colspec_spec.rb
+++ b/spec/oasis/etm/colspec_spec.rb
@@ -43,7 +43,7 @@
end
it "generates valid XML" do
- expect(colspec.to_xml).to be_equivalent_to(xml)
+ expect(colspec.to_xml).to be_analogous_with(xml)
end
end
end
diff --git a/spec/oasis/etm/entry_spec.rb b/spec/oasis/etm/entry_spec.rb
index 62d73e5..84e2a65 100644
--- a/spec/oasis/etm/entry_spec.rb
+++ b/spec/oasis/etm/entry_spec.rb
@@ -1,3 +1,4 @@
+# spec/oasis/etm/entry_spec.rb
RSpec.describe Oasis::Etm::Entry do
let(:xml) do
<<~XML
@@ -54,33 +55,49 @@
end
it "generates valid XML" do
- expect(entry.to_xml).to be_equivalent_to(xml)
+ expect(entry.to_xml).to be_analogous_with(xml)
end
end
context "with validation" do
it "validates align values" do
- expect {
- described_class.new(align: "invalid")
- }.to raise_error(Lutaml::Model::ValidationError)
+ entry = described_class.new(align: "invalid")
+ errors = entry.validate
+ expect(errors).to match_array(
+ have_attributes(
+ message: /align is `invalid`, must be one of the following \[left, right, center, justify, char\]/,
+ ),
+ )
end
it "validates valign values" do
- expect {
- described_class.new(valign: "invalid")
- }.to raise_error(Lutaml::Model::ValidationError)
+ entry = described_class.new(valign: "invalid")
+ errors = entry.validate
+ expect(errors).to match_array(
+ have_attributes(
+ message: /valign is `invalid`, must be one of the following \[top, middle, bottom\]/,
+ ),
+ )
end
it "validates colsep values" do
- expect {
- described_class.new(colsep: 2)
- }.to raise_error(Lutaml::Model::ValidationError)
+ entry = described_class.new(colsep: 2)
+ errors = entry.validate
+ expect(errors).to match_array(
+ have_attributes(
+ message: /colsep is `2`, must be one of the following \[0, 1\]/,
+ ),
+ )
end
it "validates rowsep values" do
- expect {
- described_class.new(rowsep: 2)
- }.to raise_error(Lutaml::Model::ValidationError)
+ entry = described_class.new(rowsep: 2)
+ errors = entry.validate
+ expect(errors).to match_array(
+ have_attributes(
+ message: /rowsep is `2`, must be one of the following \[0, 1\]/,
+ ),
+ )
end
end
end
diff --git a/spec/oasis/etm/row_spec.rb b/spec/oasis/etm/row_spec.rb
index caf0895..e0c1d2d 100644
--- a/spec/oasis/etm/row_spec.rb
+++ b/spec/oasis/etm/row_spec.rb
@@ -37,7 +37,7 @@
end
it "generates valid XML" do
- expect(row.to_xml).to be_equivalent_to(xml)
+ expect(row.to_xml).to be_analogous_with(xml)
end
end
end
diff --git a/spec/oasis/etm/table_spec.rb b/spec/oasis/etm/table_spec.rb
index 758c1a8..38177d0 100644
--- a/spec/oasis/etm/table_spec.rb
+++ b/spec/oasis/etm/table_spec.rb
@@ -88,7 +88,7 @@
end
it "generates valid XML" do
- expect(table.to_xml).to be_equivalent_to(xml)
+ expect(table.to_xml).to be_analogous_with(xml)
end
end
end
diff --git a/spec/oasis/etm/tbody_spec.rb b/spec/oasis/etm/tbody_spec.rb
index dcfff3c..7a1634e 100644
--- a/spec/oasis/etm/tbody_spec.rb
+++ b/spec/oasis/etm/tbody_spec.rb
@@ -42,7 +42,7 @@
end
it "generates valid XML" do
- expect(tbody.to_xml).to be_equivalent_to(xml)
+ expect(tbody.to_xml).to be_analogous_with(xml)
end
end
end
diff --git a/spec/oasis/etm/tgroup_spec.rb b/spec/oasis/etm/tgroup_spec.rb
index 04866ab..4843e89 100644
--- a/spec/oasis/etm/tgroup_spec.rb
+++ b/spec/oasis/etm/tgroup_spec.rb
@@ -91,7 +91,7 @@
end
it "generates valid XML" do
- expect(tgroup.to_xml).to be_equivalent_to(xml)
+ expect(tgroup.to_xml).to be_analogous_with(xml)
end
end
end
diff --git a/spec/oasis/etm/thead_spec.rb b/spec/oasis/etm/thead_spec.rb
index 7da3f04..4d1ff11 100644
--- a/spec/oasis/etm/thead_spec.rb
+++ b/spec/oasis/etm/thead_spec.rb
@@ -42,7 +42,7 @@
end
it "generates valid XML" do
- expect(thead.to_xml).to be_equivalent_to(xml)
+ expect(thead.to_xml).to be_analogous_with(xml)
end
end
end
diff --git a/spec/oasis/etm_spec.rb b/spec/oasis/etm_spec.rb
index d442661..39266c7 100644
--- a/spec/oasis/etm_spec.rb
+++ b/spec/oasis/etm_spec.rb
@@ -1,11 +1,81 @@
# frozen_string_literal: true
+require "pathname"
+
RSpec.describe Oasis::Etm do
- it "has a version number" do
- expect(Oasis::Etm::VERSION).not_to be nil
- end
+ fixtures_dir = Pathname.new(__dir__).join("../fixtures")
+
+ describe "XML round-trip conversion" do
+ describe "native XML" do
+ xml_files = Dir[fixtures_dir.join("native", "*.xml")]
+
+ xml_files.each do |file_path|
+ context "with file #{Pathname.new(file_path).relative_path_from(fixtures_dir)}" do
+ let(:xml_string) { File.read(file_path) }
+
+ it "performs lossless round-trip conversion" do
+ parsed = Oasis::Etm::Table.from_xml(xml_string)
+ generated = parsed.to_xml(
+ pretty: true,
+ declaration: true,
+ encoding: "utf-8",
+ )
+
+ cleaned_xml_string = xml_string
+ .gsub(/^<\?xml.*\n/, "")
+
+ expect(generated).to be_analogous_with(cleaned_xml_string)
+ end
+ end
+ end
+ end
+
+ describe "namespaced XML (ISOSTS)" do
+ xml_files = Dir[fixtures_dir.join("isosts", "*.xml")]
+
+ xml_files.each do |file_path|
+ context "with file #{Pathname.new(file_path).relative_path_from(fixtures_dir)}" do
+ let(:xml_string) { File.read(file_path) }
+
+ it "performs lossless round-trip conversion" do
+ parsed = Oasis::Etm::Table.from_xml(xml_string)
+ generated = parsed.to_xml(
+ pretty: true,
+ declaration: true,
+ encoding: "utf-8",
+ )
+
+ cleaned_xml_string = xml_string
+ .gsub(/^<\?xml.*\n/, "")
+
+ expect(generated).to be_analogous_with(cleaned_xml_string)
+ end
+ end
+ end
+ end
+
+ describe "namespaced XML (NISO JATS)" do
+ xml_files = Dir[fixtures_dir.join("niso-jats", "*.xml")]
+
+ xml_files.each do |file_path|
+ context "with file #{Pathname.new(file_path).relative_path_from(fixtures_dir)}" do
+ let(:xml_string) { File.read(file_path) }
+
+ it "performs lossless round-trip conversion" do
+ parsed = Oasis::Etm::Table.from_xml(xml_string)
+ generated = parsed.to_xml(
+ pretty: true,
+ declaration: true,
+ encoding: "utf-8",
+ )
+
+ cleaned_xml_string = xml_string
+ .gsub(/^<\?xml.*\n/, "")
- it "does something useful" do
- expect(false).to eq(true)
+ expect(generated).to be_analogous_with(cleaned_xml_string)
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 14a2ed3..2d7874d 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,9 +1,11 @@
-# spec/spec_helper.rb
-require "bundler/setup"
+# frozen_string_literal: true
+
require "oasis-etm"
+require "nokogiri"
+require "xml-c14n"
# Require all support files
-Dir[File.join(__dir__, "support", "**", "*.rb")].sort.each { |f| require f }
+Dir[File.join(__dir__, "support", "**", "*.rb")].each { |f| require f }
RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
@@ -16,3 +18,14 @@
c.syntax = :expect
end
end
+
+require "lutaml/model"
+require "lutaml/model/xml_adapter/nokogiri_adapter"
+require "lutaml/model/json_adapter/standard_json_adapter"
+require "lutaml/model/yaml_adapter/standard_yaml_adapter"
+
+Lutaml::Model::Config.configure do |config|
+ config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
+ config.json_adapter = Lutaml::Model::JsonAdapter::StandardJsonAdapter
+ config.yaml_adapter = Lutaml::Model::YamlAdapter::StandardYamlAdapter
+end
diff --git a/spec/support/shared_examples/validation_examples.rb b/spec/support/shared_examples/validation_examples.rb
index 64e0cf3..18ebfb7 100644
--- a/spec/support/shared_examples/validation_examples.rb
+++ b/spec/support/shared_examples/validation_examples.rb
@@ -4,16 +4,16 @@
context "with #{attribute}" do
it "accepts valid values" do
valid_values.each do |value|
- expect {
+ expect do
described_class.new(attribute => value)
- }.not_to raise_error
+ end.not_to raise_error
end
end
it "rejects invalid values" do
- expect {
+ expect do
described_class.new(attribute => "invalid")
- }.to raise_error(Lutaml::Model::ValidationError)
+ end.to raise_error(Lutaml::Model::ValidationError)
end
end
end