pubid 1.15.21 → 2.0.0.alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.adoc +2044 -53
- data/archived-gems/pubid-ccsds/update_codes.yaml +1 -0
- data/archived-gems/pubid-iec/stages.yaml +129 -0
- data/archived-gems/pubid-iec/update_codes.yaml +67 -0
- data/archived-gems/pubid-ieee/update_codes.yaml +104 -0
- data/archived-gems/pubid-iso/stages.yaml +106 -0
- data/archived-gems/pubid-iso/update_codes.yaml +4 -0
- data/archived-gems/pubid-itu/i18n.yaml +13 -0
- data/archived-gems/pubid-itu/series.yaml +42 -0
- data/archived-gems/pubid-nist/publishers.yaml +6 -0
- data/archived-gems/pubid-nist/series.yaml +121 -0
- data/archived-gems/pubid-nist/stages.yaml +16 -0
- data/archived-gems/pubid-nist/update_codes.yaml +93 -0
- data/archived-gems/pubid-plateau/update_codes.yaml +6 -0
- data/data/bipm/update_codes.yaml +9 -0
- data/data/ccsds/update_codes.yaml +1 -0
- data/data/iec/update_codes.yaml +70 -0
- data/data/ieee/update_codes.yaml +210 -0
- data/data/iso/update_codes.yaml +21 -0
- data/data/nist/series.yaml +155 -0
- data/data/nist/update_codes.yaml +122 -0
- data/data/plateau/update_codes.yaml +6 -0
- data/lib/pubid/adobe/builder.rb +52 -0
- data/lib/pubid/adobe/identifier.rb +47 -0
- data/lib/pubid/adobe/identifiers/publication.rb +31 -0
- data/lib/pubid/adobe/identifiers/tech_note.rb +32 -0
- data/lib/pubid/adobe/identifiers.rb +10 -0
- data/lib/pubid/adobe/parser.rb +136 -0
- data/lib/pubid/adobe/renderer.rb +39 -0
- data/lib/pubid/adobe/urn_generator.rb +42 -0
- data/lib/pubid/adobe/urn_parser.rb +69 -0
- data/lib/pubid/adobe.rb +60 -0
- data/lib/pubid/amca/builder.rb +176 -0
- data/lib/pubid/amca/identifier.rb +4 -0
- data/lib/pubid/amca/identifiers/base.rb +92 -0
- data/lib/pubid/amca/identifiers/interpretation.rb +38 -0
- data/lib/pubid/amca/identifiers/publication.rb +34 -0
- data/lib/pubid/amca/identifiers/standard.rb +20 -0
- data/lib/pubid/amca/identifiers.rb +11 -0
- data/lib/pubid/amca/parser.rb +153 -0
- data/lib/pubid/amca/renderer.rb +82 -0
- data/lib/pubid/amca/single_identifier.rb +10 -0
- data/lib/pubid/amca/urn_generator.rb +51 -0
- data/lib/pubid/amca/urn_parser.rb +28 -0
- data/lib/pubid/amca.rb +73 -0
- data/lib/pubid/ansi/builder.rb +54 -0
- data/lib/pubid/ansi/identifier.rb +31 -0
- data/lib/pubid/ansi/identifiers/american_national_standard.rb +13 -0
- data/lib/pubid/ansi/identifiers/standard.rb +15 -0
- data/lib/pubid/ansi/identifiers.rb +11 -0
- data/lib/pubid/ansi/parser.rb +91 -0
- data/lib/pubid/ansi/renderer.rb +53 -0
- data/lib/pubid/ansi/single_identifier.rb +16 -0
- data/lib/pubid/ansi/urn_generator.rb +41 -0
- data/lib/pubid/ansi/urn_parser.rb +23 -0
- data/lib/pubid/ansi.rb +68 -0
- data/lib/pubid/api/builder.rb +58 -0
- data/lib/pubid/api/components/code.rb +9 -0
- data/lib/pubid/api/identifier.rb +42 -0
- data/lib/pubid/api/identifiers/base.rb +12 -0
- data/lib/pubid/api/identifiers/bulletin.rb +13 -0
- data/lib/pubid/api/identifiers/continuous_operations_standard.rb +13 -0
- data/lib/pubid/api/identifiers/mpms.rb +26 -0
- data/lib/pubid/api/identifiers/publication.rb +13 -0
- data/lib/pubid/api/identifiers/recommended_practice.rb +13 -0
- data/lib/pubid/api/identifiers/specification.rb +13 -0
- data/lib/pubid/api/identifiers/standard.rb +13 -0
- data/lib/pubid/api/identifiers/technical_report.rb +13 -0
- data/lib/pubid/api/identifiers/typeless_standard.rb +11 -0
- data/lib/pubid/api/identifiers.rb +18 -0
- data/lib/pubid/api/parser.rb +140 -0
- data/lib/pubid/api/renderer.rb +98 -0
- data/lib/pubid/api/single_identifier.rb +24 -0
- data/lib/pubid/api/urn_generator.rb +23 -0
- data/lib/pubid/api/urn_parser.rb +35 -0
- data/lib/pubid/api.rb +68 -0
- data/lib/pubid/ashrae/builder.rb +519 -0
- data/lib/pubid/ashrae/identifier.rb +8 -0
- data/lib/pubid/ashrae/identifiers/addenda_package.rb +42 -0
- data/lib/pubid/ashrae/identifiers/addendum.rb +44 -0
- data/lib/pubid/ashrae/identifiers/base.rb +119 -0
- data/lib/pubid/ashrae/identifiers/combined_addenda.rb +43 -0
- data/lib/pubid/ashrae/identifiers/errata.rb +39 -0
- data/lib/pubid/ashrae/identifiers/guideline.rb +41 -0
- data/lib/pubid/ashrae/identifiers/interpretation.rb +45 -0
- data/lib/pubid/ashrae/identifiers/standard.rb +41 -0
- data/lib/pubid/ashrae/identifiers.rb +15 -0
- data/lib/pubid/ashrae/parser.rb +748 -0
- data/lib/pubid/ashrae/renderer.rb +117 -0
- data/lib/pubid/ashrae/single_identifier.rb +10 -0
- data/lib/pubid/ashrae/supplement_identifier.rb +38 -0
- data/lib/pubid/ashrae/urn_generator.rb +57 -0
- data/lib/pubid/ashrae/urn_parser.rb +27 -0
- data/lib/pubid/ashrae.rb +77 -0
- data/lib/pubid/asme/builder.rb +162 -0
- data/lib/pubid/asme/components/code.rb +26 -0
- data/lib/pubid/asme/identifier.rb +24 -0
- data/lib/pubid/asme/identifiers/base.rb +10 -0
- data/lib/pubid/asme/identifiers/standard.rb +96 -0
- data/lib/pubid/asme/identifiers.rb +10 -0
- data/lib/pubid/asme/parser.rb +308 -0
- data/lib/pubid/asme/renderer.rb +66 -0
- data/lib/pubid/asme/single_identifier.rb +38 -0
- data/lib/pubid/asme/urn_generator.rb +133 -0
- data/lib/pubid/asme/urn_parser.rb +31 -0
- data/lib/pubid/asme.rb +67 -0
- data/lib/pubid/astm/builder.rb +166 -0
- data/lib/pubid/astm/components/code.rb +42 -0
- data/lib/pubid/astm/components.rb +9 -0
- data/lib/pubid/astm/identifier.rb +24 -0
- data/lib/pubid/astm/identifiers/adjunct.rb +28 -0
- data/lib/pubid/astm/identifiers/base.rb +13 -0
- data/lib/pubid/astm/identifiers/code_number.rb +81 -0
- data/lib/pubid/astm/identifiers/data_series.rb +13 -0
- data/lib/pubid/astm/identifiers/iso_dual_published.rb +65 -0
- data/lib/pubid/astm/identifiers/manual.rb +23 -0
- data/lib/pubid/astm/identifiers/monograph.rb +13 -0
- data/lib/pubid/astm/identifiers/research_report.rb +21 -0
- data/lib/pubid/astm/identifiers/standard.rb +23 -0
- data/lib/pubid/astm/identifiers/technical_report.rb +12 -0
- data/lib/pubid/astm/identifiers/work_in_progress.rb +12 -0
- data/lib/pubid/astm/identifiers.rb +19 -0
- data/lib/pubid/astm/parser.rb +244 -0
- data/lib/pubid/astm/renderer.rb +172 -0
- data/lib/pubid/astm/single_identifier.rb +47 -0
- data/lib/pubid/astm/urn_generator.rb +101 -0
- data/lib/pubid/astm/urn_parser.rb +30 -0
- data/lib/pubid/astm.rb +55 -0
- data/lib/pubid/bipm/builder.rb +163 -0
- data/lib/pubid/bipm/identifier.rb +285 -0
- data/lib/pubid/bipm/identifiers/committee_document.rb +36 -0
- data/lib/pubid/bipm/identifiers/guide.rb +35 -0
- data/lib/pubid/bipm/identifiers/meeting.rb +53 -0
- data/lib/pubid/bipm/identifiers/mep.rb +33 -0
- data/lib/pubid/bipm/identifiers/metrologia_article.rb +48 -0
- data/lib/pubid/bipm/identifiers/si_brochure.rb +33 -0
- data/lib/pubid/bipm/identifiers.rb +15 -0
- data/lib/pubid/bipm/parser.rb +237 -0
- data/lib/pubid/bipm/renderer.rb +118 -0
- data/lib/pubid/bipm/urn_generator.rb +58 -0
- data/lib/pubid/bipm/urn_parser.rb +60 -0
- data/lib/pubid/bipm.rb +91 -0
- data/lib/pubid/bsi/builder.rb +1495 -0
- data/lib/pubid/bsi/components/code.rb +11 -0
- data/lib/pubid/bsi/components/date.rb +11 -0
- data/lib/pubid/bsi/components/publisher.rb +11 -0
- data/lib/pubid/bsi/components/type.rb +11 -0
- data/lib/pubid/bsi/identifier.rb +6 -0
- data/lib/pubid/bsi/identifiers/addendum_document.rb +30 -0
- data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +67 -0
- data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +51 -0
- data/lib/pubid/bsi/identifiers/aerospace_standard.rb +65 -0
- data/lib/pubid/bsi/identifiers/amendment.rb +48 -0
- data/lib/pubid/bsi/identifiers/british_industrial_practice.rb +21 -0
- data/lib/pubid/bsi/identifiers/british_standard.rb +34 -0
- data/lib/pubid/bsi/identifiers/bundled_identifier.rb +40 -0
- data/lib/pubid/bsi/identifiers/committee_document.rb +44 -0
- data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +84 -0
- data/lib/pubid/bsi/identifiers/corrigendum.rb +44 -0
- data/lib/pubid/bsi/identifiers/detailed_specification.rb +33 -0
- data/lib/pubid/bsi/identifiers/disc.rb +27 -0
- data/lib/pubid/bsi/identifiers/draft_document.rb +27 -0
- data/lib/pubid/bsi/identifiers/electronic_book.rb +16 -0
- data/lib/pubid/bsi/identifiers/expert_commentary.rb +36 -0
- data/lib/pubid/bsi/identifiers/explanatory_supplement.rb +35 -0
- data/lib/pubid/bsi/identifiers/flex.rb +30 -0
- data/lib/pubid/bsi/identifiers/handbook.rb +23 -0
- data/lib/pubid/bsi/identifiers/index.rb +30 -0
- data/lib/pubid/bsi/identifiers/method.rb +35 -0
- data/lib/pubid/bsi/identifiers/national_annex.rb +48 -0
- data/lib/pubid/bsi/identifiers/practice_guide.rb +21 -0
- data/lib/pubid/bsi/identifiers/publicly_available_specification.rb +27 -0
- data/lib/pubid/bsi/identifiers/published_document.rb +27 -0
- data/lib/pubid/bsi/identifiers/section.rb +32 -0
- data/lib/pubid/bsi/identifiers/set.rb +40 -0
- data/lib/pubid/bsi/identifiers/standalone_amendment.rb +40 -0
- data/lib/pubid/bsi/identifiers/supplement_document.rb +29 -0
- data/lib/pubid/bsi/identifiers/supplementary_index.rb +35 -0
- data/lib/pubid/bsi/identifiers/technical_specification.rb +34 -0
- data/lib/pubid/bsi/identifiers/test_method.rb +35 -0
- data/lib/pubid/bsi/identifiers/value_added_publication.rb +37 -0
- data/lib/pubid/bsi/identifiers.rb +51 -0
- data/lib/pubid/bsi/model.rb +196 -0
- data/lib/pubid/bsi/parser.rb +673 -0
- data/lib/pubid/bsi/renderer.rb +1064 -0
- data/lib/pubid/bsi/single_identifier.rb +141 -0
- data/lib/pubid/bsi/urn_generator.rb +92 -0
- data/lib/pubid/bsi/urn_parser.rb +52 -0
- data/lib/pubid/bsi.rb +282 -0
- data/lib/pubid/builder/base.rb +187 -0
- data/lib/pubid/bundled_identifier.rb +134 -0
- data/lib/pubid/calconnect/builder.rb +38 -0
- data/lib/pubid/calconnect/identifier.rb +121 -0
- data/lib/pubid/calconnect/identifiers/standard.rb +29 -0
- data/lib/pubid/calconnect/identifiers.rb +9 -0
- data/lib/pubid/calconnect/parser.rb +54 -0
- data/lib/pubid/calconnect/renderer.rb +36 -0
- data/lib/pubid/calconnect/urn_generator.rb +33 -0
- data/lib/pubid/calconnect/urn_parser.rb +34 -0
- data/lib/pubid/calconnect.rb +82 -0
- data/lib/pubid/ccsds/builder.rb +57 -0
- data/lib/pubid/ccsds/identifier.rb +76 -0
- data/lib/pubid/ccsds/identifiers/base.rb +41 -0
- data/lib/pubid/ccsds/identifiers/base_BASE_88929.rb +70 -0
- data/lib/pubid/ccsds/identifiers/corrigendum.rb +40 -0
- data/lib/pubid/ccsds/identifiers.rb +10 -0
- data/lib/pubid/ccsds/parser.rb +73 -0
- data/lib/pubid/ccsds/single_identifier.rb +77 -0
- data/lib/pubid/ccsds/supplement_identifier.rb +37 -0
- data/lib/pubid/ccsds/urn_generator.rb +115 -0
- data/lib/pubid/ccsds/urn_parser.rb +20 -0
- data/lib/pubid/ccsds.rb +64 -0
- data/lib/pubid/cen_cenelec/builder.rb +362 -0
- data/lib/pubid/cen_cenelec/identifier.rb +38 -0
- data/lib/pubid/cen_cenelec/identifiers/adopted_european_norm.rb +45 -0
- data/lib/pubid/cen_cenelec/identifiers/amendment.rb +19 -0
- data/lib/pubid/cen_cenelec/identifiers/base.rb +34 -0
- data/lib/pubid/cen_cenelec/identifiers/cen_report.rb +29 -0
- data/lib/pubid/cen_cenelec/identifiers/cen_workshop_agreement.rb +28 -0
- data/lib/pubid/cen_cenelec/identifiers/cenelec_harmonization_document.rb +28 -0
- data/lib/pubid/cen_cenelec/identifiers/consolidated_identifier.rb +40 -0
- data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +20 -0
- data/lib/pubid/cen_cenelec/identifiers/european_norm.rb +99 -0
- data/lib/pubid/cen_cenelec/identifiers/european_prestandard.rb +50 -0
- data/lib/pubid/cen_cenelec/identifiers/european_specification.rb +29 -0
- data/lib/pubid/cen_cenelec/identifiers/fragment.rb +18 -0
- data/lib/pubid/cen_cenelec/identifiers/guide.rb +27 -0
- data/lib/pubid/cen_cenelec/identifiers/harmonization_document.rb +28 -0
- data/lib/pubid/cen_cenelec/identifiers/technical_report.rb +28 -0
- data/lib/pubid/cen_cenelec/identifiers/technical_specification.rb +36 -0
- data/lib/pubid/cen_cenelec/identifiers.rb +32 -0
- data/lib/pubid/cen_cenelec/parser.rb +144 -0
- data/lib/pubid/cen_cenelec/renderer.rb +261 -0
- data/lib/pubid/cen_cenelec/single_identifier.rb +49 -0
- data/lib/pubid/cen_cenelec/supplement_identifier.rb +47 -0
- data/lib/pubid/cen_cenelec/urn_generator.rb +129 -0
- data/lib/pubid/cen_cenelec/urn_parser.rb +28 -0
- data/lib/pubid/cen_cenelec.rb +208 -0
- data/lib/pubid/cie/builder.rb +524 -0
- data/lib/pubid/cie/components/language.rb +58 -0
- data/lib/pubid/cie/identifier.rb +33 -0
- data/lib/pubid/cie/identifiers/bundle.rb +50 -0
- data/lib/pubid/cie/identifiers/code_attributes.rb +52 -0
- data/lib/pubid/cie/identifiers/conference.rb +37 -0
- data/lib/pubid/cie/identifiers/corrigendum.rb +34 -0
- data/lib/pubid/cie/identifiers/dual_published.rb +41 -0
- data/lib/pubid/cie/identifiers/identical.rb +64 -0
- data/lib/pubid/cie/identifiers/joint_published.rb +52 -0
- data/lib/pubid/cie/identifiers/proceedings.rb +41 -0
- data/lib/pubid/cie/identifiers/standard.rb +93 -0
- data/lib/pubid/cie/identifiers/supplement.rb +47 -0
- data/lib/pubid/cie/identifiers/tutorial_bundle.rb +24 -0
- data/lib/pubid/cie/identifiers.rb +19 -0
- data/lib/pubid/cie/parser.rb +396 -0
- data/lib/pubid/cie/single_identifier.rb +69 -0
- data/lib/pubid/cie/supplement_identifier.rb +39 -0
- data/lib/pubid/cie/urn_generator.rb +126 -0
- data/lib/pubid/cie/urn_parser.rb +28 -0
- data/lib/pubid/cie.rb +83 -0
- data/lib/pubid/components/adoption.rb +104 -0
- data/lib/pubid/components/code.rb +47 -0
- data/lib/pubid/components/date.rb +72 -0
- data/lib/pubid/components/edition.rb +35 -0
- data/lib/pubid/components/iteration.rb +32 -0
- data/lib/pubid/components/language.rb +39 -0
- data/lib/pubid/components/locality.rb +19 -0
- data/lib/pubid/components/publisher.rb +39 -0
- data/lib/pubid/components/relationship.rb +151 -0
- data/lib/pubid/components/stage.rb +45 -0
- data/lib/pubid/components/supplement.rb +184 -0
- data/lib/pubid/components/type.rb +48 -0
- data/lib/pubid/components/typed_stage.rb +58 -0
- data/lib/pubid/components.rb +19 -0
- data/lib/pubid/core/pattern_doc_generator.rb +272 -0
- data/lib/pubid/core/update_codes.rb +98 -0
- data/lib/pubid/core.rb +8 -0
- data/lib/pubid/csa/builder.rb +671 -0
- data/lib/pubid/csa/components/code.rb +9 -0
- data/lib/pubid/csa/components.rb +9 -0
- data/lib/pubid/csa/composite_identifier.rb +38 -0
- data/lib/pubid/csa/identifier.rb +570 -0
- data/lib/pubid/csa/identifiers/base.rb +10 -0
- data/lib/pubid/csa/identifiers/bundled.rb +141 -0
- data/lib/pubid/csa/identifiers/canadian_adopted.rb +82 -0
- data/lib/pubid/csa/identifiers/cec.rb +66 -0
- data/lib/pubid/csa/identifiers/combined.rb +158 -0
- data/lib/pubid/csa/identifiers/csa_adopted.rb +78 -0
- data/lib/pubid/csa/identifiers/package.rb +65 -0
- data/lib/pubid/csa/identifiers/series.rb +23 -0
- data/lib/pubid/csa/identifiers/standard.rb +10 -0
- data/lib/pubid/csa/identifiers.rb +17 -0
- data/lib/pubid/csa/parser.rb +449 -0
- data/lib/pubid/csa/renderer.rb +300 -0
- data/lib/pubid/csa/single_identifier.rb +119 -0
- data/lib/pubid/csa/urn_generator.rb +87 -0
- data/lib/pubid/csa/urn_parser.rb +33 -0
- data/lib/pubid/csa/wrapper_identifier.rb +64 -0
- data/lib/pubid/csa.rb +80 -0
- data/lib/pubid/doi/builder.rb +18 -0
- data/lib/pubid/doi/identifier.rb +49 -0
- data/lib/pubid/doi/identifiers/resource.rb +30 -0
- data/lib/pubid/doi/identifiers.rb +9 -0
- data/lib/pubid/doi/parser.rb +52 -0
- data/lib/pubid/doi/renderer.rb +13 -0
- data/lib/pubid/doi.rb +55 -0
- data/lib/pubid/easc/builder.rb +55 -0
- data/lib/pubid/easc/identifier.rb +59 -0
- data/lib/pubid/easc/identifiers/pmg.rb +20 -0
- data/lib/pubid/easc/identifiers/rmg.rb +19 -0
- data/lib/pubid/easc/identifiers.rb +10 -0
- data/lib/pubid/easc/parser.rb +88 -0
- data/lib/pubid/easc/renderer.rb +48 -0
- data/lib/pubid/easc/urn_generator.rb +46 -0
- data/lib/pubid/easc/urn_parser.rb +57 -0
- data/lib/pubid/easc.rb +52 -0
- data/lib/pubid/ecma/builder.rb +45 -0
- data/lib/pubid/ecma/identifier.rb +147 -0
- data/lib/pubid/ecma/identifiers/memento.rb +30 -0
- data/lib/pubid/ecma/identifiers/standard.rb +31 -0
- data/lib/pubid/ecma/identifiers/technical_report.rb +31 -0
- data/lib/pubid/ecma/identifiers.rb +11 -0
- data/lib/pubid/ecma/parser.rb +76 -0
- data/lib/pubid/ecma/renderer.rb +67 -0
- data/lib/pubid/ecma/urn_generator.rb +46 -0
- data/lib/pubid/ecma/urn_parser.rb +51 -0
- data/lib/pubid/ecma.rb +67 -0
- data/lib/pubid/errors.rb +79 -0
- data/lib/pubid/etsi/builder.rb +142 -0
- data/lib/pubid/etsi/components/code.rb +43 -0
- data/lib/pubid/etsi/components/version.rb +32 -0
- data/lib/pubid/etsi/components.rb +10 -0
- data/lib/pubid/etsi/identifier.rb +4 -0
- data/lib/pubid/etsi/identifiers/amendment.rb +15 -0
- data/lib/pubid/etsi/identifiers/base.rb +74 -0
- data/lib/pubid/etsi/identifiers/corrigendum.rb +15 -0
- data/lib/pubid/etsi/identifiers/etsi_standard.rb +222 -0
- data/lib/pubid/etsi/identifiers/supplement_identifier.rb +123 -0
- data/lib/pubid/etsi/identifiers.rb +13 -0
- data/lib/pubid/etsi/parser.rb +139 -0
- data/lib/pubid/etsi/renderer.rb +48 -0
- data/lib/pubid/etsi/urn_generator.rb +76 -0
- data/lib/pubid/etsi/urn_parser.rb +34 -0
- data/lib/pubid/etsi.rb +71 -0
- data/lib/pubid/export/auditor.rb +89 -0
- data/lib/pubid/export/exporter.rb +33 -0
- data/lib/pubid/export/flavor_exporter.rb +250 -0
- data/lib/pubid/export/result.rb +97 -0
- data/lib/pubid/export.rb +12 -0
- data/lib/pubid/format_detector.rb +16 -0
- data/lib/pubid/format_registry.rb +42 -0
- data/lib/pubid/gb/builder.rb +45 -0
- data/lib/pubid/gb/identifier.rb +67 -0
- data/lib/pubid/gb/identifiers/standard.rb +34 -0
- data/lib/pubid/gb/identifiers.rb +9 -0
- data/lib/pubid/gb/parser.rb +63 -0
- data/lib/pubid/gb/renderer.rb +35 -0
- data/lib/pubid/gb.rb +87 -0
- data/lib/pubid/gost/builder.rb +148 -0
- data/lib/pubid/gost/identifier.rb +47 -0
- data/lib/pubid/gost/identifiers/foreign_reference.rb +25 -0
- data/lib/pubid/gost/identifiers/harmonized.rb +39 -0
- data/lib/pubid/gost/identifiers/identical_adoption.rb +39 -0
- data/lib/pubid/gost/identifiers/interstate_standard.rb +20 -0
- data/lib/pubid/gost/identifiers/national_standard.rb +19 -0
- data/lib/pubid/gost/identifiers.rb +13 -0
- data/lib/pubid/gost/parser.rb +105 -0
- data/lib/pubid/gost/renderer.rb +48 -0
- data/lib/pubid/gost/urn_generator.rb +46 -0
- data/lib/pubid/gost/urn_parser.rb +52 -0
- data/lib/pubid/gost.rb +49 -0
- data/lib/pubid/iala/builder.rb +102 -0
- data/lib/pubid/iala/identifier.rb +90 -0
- data/lib/pubid/iala/identifiers/advice.rb +15 -0
- data/lib/pubid/iala/identifiers/annex.rb +57 -0
- data/lib/pubid/iala/identifiers/general_assembly.rb +19 -0
- data/lib/pubid/iala/identifiers/guideline.rb +17 -0
- data/lib/pubid/iala/identifiers/letter.rb +16 -0
- data/lib/pubid/iala/identifiers/manual.rb +18 -0
- data/lib/pubid/iala/identifiers/model_course.rb +17 -0
- data/lib/pubid/iala/identifiers/recommendation.rb +17 -0
- data/lib/pubid/iala/identifiers/report.rb +16 -0
- data/lib/pubid/iala/identifiers/resolution.rb +16 -0
- data/lib/pubid/iala/identifiers/standard.rb +17 -0
- data/lib/pubid/iala/identifiers.rb +19 -0
- data/lib/pubid/iala/parser.rb +138 -0
- data/lib/pubid/iala/renderer.rb +36 -0
- data/lib/pubid/iala/urn_generator.rb +49 -0
- data/lib/pubid/iala/urn_parser.rb +67 -0
- data/lib/pubid/iala.rb +71 -0
- data/lib/pubid/iana/builder.rb +22 -0
- data/lib/pubid/iana/identifier.rb +140 -0
- data/lib/pubid/iana/identifiers/registry.rb +69 -0
- data/lib/pubid/iana/identifiers.rb +9 -0
- data/lib/pubid/iana/parser.rb +38 -0
- data/lib/pubid/iana/renderer.rb +32 -0
- data/lib/pubid/iana/urn_generator.rb +19 -0
- data/lib/pubid/iana/urn_parser.rb +24 -0
- data/lib/pubid/iana.rb +69 -0
- data/lib/pubid/identifier.rb +1118 -0
- data/lib/pubid/identifier_metadata.rb +148 -0
- data/lib/pubid/idf/builder.rb +82 -0
- data/lib/pubid/idf/identifier.rb +79 -0
- data/lib/pubid/idf/identifiers/amendment.rb +28 -0
- data/lib/pubid/idf/identifiers/corrigendum.rb +28 -0
- data/lib/pubid/idf/identifiers/international_standard.rb +124 -0
- data/lib/pubid/idf/identifiers/reviewed_method.rb +101 -0
- data/lib/pubid/idf/identifiers.rb +13 -0
- data/lib/pubid/idf/parser.rb +144 -0
- data/lib/pubid/idf/renderer.rb +84 -0
- data/lib/pubid/idf/single_identifier.rb +19 -0
- data/lib/pubid/idf/supplement_identifier.rb +35 -0
- data/lib/pubid/idf/urn_generator.rb +49 -0
- data/lib/pubid/idf/urn_parser.rb +25 -0
- data/lib/pubid/idf.rb +89 -0
- data/lib/pubid/iec/builder.rb +458 -0
- data/lib/pubid/iec/components/code.rb +36 -0
- data/lib/pubid/iec/components/consolidated_amendment.rb +59 -0
- data/lib/pubid/iec/components/publisher.rb +35 -0
- data/lib/pubid/iec/components/sheet.rb +32 -0
- data/lib/pubid/iec/components/trf_info.rb +38 -0
- data/lib/pubid/iec/components/vap_suffix.rb +41 -0
- data/lib/pubid/iec/components.rb +14 -0
- data/lib/pubid/iec/identifier.rb +341 -0
- data/lib/pubid/iec/identifiers/amendment.rb +97 -0
- data/lib/pubid/iec/identifiers/base.rb +54 -0
- data/lib/pubid/iec/identifiers/component_specification.rb +29 -0
- data/lib/pubid/iec/identifiers/conformity_assessment.rb +28 -0
- data/lib/pubid/iec/identifiers/consolidated_identifier.rb +85 -0
- data/lib/pubid/iec/identifiers/corrigendum.rb +93 -0
- data/lib/pubid/iec/identifiers/fragment_identifier.rb +148 -0
- data/lib/pubid/iec/identifiers/guide.rb +86 -0
- data/lib/pubid/iec/identifiers/international_standard.rb +162 -0
- data/lib/pubid/iec/identifiers/interpretation_sheet.rb +103 -0
- data/lib/pubid/iec/identifiers/operational_document.rb +29 -0
- data/lib/pubid/iec/identifiers/publicly_available_specification.rb +88 -0
- data/lib/pubid/iec/identifiers/sheet_identifier.rb +68 -0
- data/lib/pubid/iec/identifiers/societal_technology_trend_report.rb +30 -0
- data/lib/pubid/iec/identifiers/systems_reference_document.rb +29 -0
- data/lib/pubid/iec/identifiers/technical_group.rb +30 -0
- data/lib/pubid/iec/identifiers/technical_report.rb +114 -0
- data/lib/pubid/iec/identifiers/technical_specification.rb +114 -0
- data/lib/pubid/iec/identifiers/technology_report.rb +28 -0
- data/lib/pubid/iec/identifiers/test_report_form.rb +35 -0
- data/lib/pubid/iec/identifiers/vap_identifier.rb +76 -0
- data/lib/pubid/iec/identifiers/white_paper.rb +29 -0
- data/lib/pubid/iec/identifiers/working_document.rb +62 -0
- data/lib/pubid/iec/identifiers.rb +31 -0
- data/lib/pubid/iec/parser.rb +443 -0
- data/lib/pubid/iec/renderer.rb +267 -0
- data/lib/pubid/iec/rendering_style.rb +113 -0
- data/lib/pubid/iec/single_identifier.rb +72 -0
- data/lib/pubid/iec/supplement_identifier.rb +178 -0
- data/lib/pubid/iec/urn_generator.rb +245 -0
- data/lib/pubid/iec/urn_parser.rb +128 -0
- data/lib/pubid/iec.rb +64 -0
- data/lib/pubid/ieee/aiee/builder.rb +85 -0
- data/lib/pubid/ieee/aiee/identifier.rb +89 -0
- data/lib/pubid/ieee/aiee/parser.rb +130 -0
- data/lib/pubid/ieee/aiee.rb +11 -0
- data/lib/pubid/ieee/builder.rb +1554 -0
- data/lib/pubid/ieee/compaction.rb +121 -0
- data/lib/pubid/ieee/components/code.rb +110 -0
- data/lib/pubid/ieee/components/draft.rb +147 -0
- data/lib/pubid/ieee/components/relationship.rb +13 -0
- data/lib/pubid/ieee/components/typed_stage.rb +100 -0
- data/lib/pubid/ieee/identifier.rb +5 -0
- data/lib/pubid/ieee/identifiers/adopted_standard.rb +81 -0
- data/lib/pubid/ieee/identifiers/amendment.rb +40 -0
- data/lib/pubid/ieee/identifiers/base.rb +384 -0
- data/lib/pubid/ieee/identifiers/code_number.rb +82 -0
- data/lib/pubid/ieee/identifiers/conformance_identifier.rb +29 -0
- data/lib/pubid/ieee/identifiers/corrigendum.rb +40 -0
- data/lib/pubid/ieee/identifiers/csa_dual_published.rb +62 -0
- data/lib/pubid/ieee/identifiers/dual_identifier.rb +14 -0
- data/lib/pubid/ieee/identifiers/dual_published.rb +79 -0
- data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +115 -0
- data/lib/pubid/ieee/identifiers/interpretation_identifier.rb +48 -0
- data/lib/pubid/ieee/identifiers/joint_development.rb +223 -0
- data/lib/pubid/ieee/identifiers/multi_numbered_identifier.rb +85 -0
- data/lib/pubid/ieee/identifiers/nesc/base.rb +127 -0
- data/lib/pubid/ieee/identifiers/nesc/draft.rb +43 -0
- data/lib/pubid/ieee/identifiers/nesc/edition.rb +32 -0
- data/lib/pubid/ieee/identifiers/nesc/handbook.rb +43 -0
- data/lib/pubid/ieee/identifiers/nesc/redline.rb +35 -0
- data/lib/pubid/ieee/identifiers/nesc/standard.rb +41 -0
- data/lib/pubid/ieee/identifiers/nesc.rb +16 -0
- data/lib/pubid/ieee/identifiers/parenthetical_identifier.rb +14 -0
- data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +49 -0
- data/lib/pubid/ieee/identifiers/redlined_standard.rb +26 -0
- data/lib/pubid/ieee/identifiers/si_standard.rb +38 -0
- data/lib/pubid/ieee/identifiers/standard.rb +44 -0
- data/lib/pubid/ieee/identifiers/supplement_identifier.rb +42 -0
- data/lib/pubid/ieee/identifiers.rb +34 -0
- data/lib/pubid/ieee/ire/builder.rb +62 -0
- data/lib/pubid/ieee/ire/identifier.rb +73 -0
- data/lib/pubid/ieee/ire/parser.rb +91 -0
- data/lib/pubid/ieee/ire.rb +11 -0
- data/lib/pubid/ieee/nesc/builder.rb +114 -0
- data/lib/pubid/ieee/nesc/parser.rb +155 -0
- data/lib/pubid/ieee/nesc.rb +10 -0
- data/lib/pubid/ieee/parser.rb +1644 -0
- data/lib/pubid/ieee/pre_parser.rb +301 -0
- data/lib/pubid/ieee/renderer.rb +408 -0
- data/lib/pubid/ieee/typed_stages.rb +183 -0
- data/lib/pubid/ieee/urn_generator.rb +215 -0
- data/lib/pubid/ieee/urn_parser.rb +34 -0
- data/lib/pubid/ieee.rb +163 -0
- data/lib/pubid/ieee_debug.rb +30 -0
- data/lib/pubid/ietf/builder.rb +78 -0
- data/lib/pubid/ietf/identifier.rb +5 -0
- data/lib/pubid/ietf/identifiers/base.rb +72 -0
- data/lib/pubid/ietf/identifiers/bcp.rb +36 -0
- data/lib/pubid/ietf/identifiers/fyi.rb +36 -0
- data/lib/pubid/ietf/identifiers/internet_draft.rb +45 -0
- data/lib/pubid/ietf/identifiers/rfc.rb +27 -0
- data/lib/pubid/ietf/identifiers/serialization.rb +51 -0
- data/lib/pubid/ietf/identifiers/std.rb +36 -0
- data/lib/pubid/ietf/identifiers.rb +14 -0
- data/lib/pubid/ietf/parser.rb +61 -0
- data/lib/pubid/ietf/renderer.rb +64 -0
- data/lib/pubid/ietf/urn_generator.rb +37 -0
- data/lib/pubid/ietf/urn_parser.rb +43 -0
- data/lib/pubid/ietf.rb +85 -0
- data/lib/pubid/iho/builder.rb +37 -0
- data/lib/pubid/iho/identifier.rb +7 -0
- data/lib/pubid/iho/identifiers/base.rb +75 -0
- data/lib/pubid/iho/identifiers/bibliographic.rb +16 -0
- data/lib/pubid/iho/identifiers/circular_letter.rb +15 -0
- data/lib/pubid/iho/identifiers/miscellaneous.rb +16 -0
- data/lib/pubid/iho/identifiers/publication.rb +15 -0
- data/lib/pubid/iho/identifiers/standard.rb +15 -0
- data/lib/pubid/iho/identifiers.rb +13 -0
- data/lib/pubid/iho/parser.rb +68 -0
- data/lib/pubid/iho/renderer.rb +30 -0
- data/lib/pubid/iho/urn_generator.rb +29 -0
- data/lib/pubid/iho/urn_parser.rb +58 -0
- data/lib/pubid/iho.rb +77 -0
- data/lib/pubid/isbn/builder.rb +44 -0
- data/lib/pubid/isbn/check_digit.rb +53 -0
- data/lib/pubid/isbn/identifier.rb +81 -0
- data/lib/pubid/isbn/identifiers/book.rb +30 -0
- data/lib/pubid/isbn/identifiers.rb +9 -0
- data/lib/pubid/isbn/parser.rb +45 -0
- data/lib/pubid/isbn/renderer.rb +16 -0
- data/lib/pubid/isbn.rb +52 -0
- data/lib/pubid/iso/builder.rb +316 -0
- data/lib/pubid/iso/bundled_identifier.rb +138 -0
- data/lib/pubid/iso/combined_identifier.rb +24 -0
- data/lib/pubid/iso/components/code.rb +24 -0
- data/lib/pubid/iso/components/publisher.rb +62 -0
- data/lib/pubid/iso/components.rb +10 -0
- data/lib/pubid/iso/format_resolver.rb +45 -0
- data/lib/pubid/iso/identifier.rb +318 -0
- data/lib/pubid/iso/identifiers/addendum.rb +107 -0
- data/lib/pubid/iso/identifiers/amendment.rb +132 -0
- data/lib/pubid/iso/identifiers/corrigendum.rb +108 -0
- data/lib/pubid/iso/identifiers/data.rb +75 -0
- data/lib/pubid/iso/identifiers/directives.rb +65 -0
- data/lib/pubid/iso/identifiers/directives_supplement.rb +148 -0
- data/lib/pubid/iso/identifiers/extract.rb +30 -0
- data/lib/pubid/iso/identifiers/guide.rb +99 -0
- data/lib/pubid/iso/identifiers/international_standard.rb +168 -0
- data/lib/pubid/iso/identifiers/international_standardized_profile.rb +94 -0
- data/lib/pubid/iso/identifiers/international_workshop_agreement.rb +95 -0
- data/lib/pubid/iso/identifiers/pas.rb +93 -0
- data/lib/pubid/iso/identifiers/recommendation.rb +45 -0
- data/lib/pubid/iso/identifiers/supplement.rb +95 -0
- data/lib/pubid/iso/identifiers/tc_document.rb +137 -0
- data/lib/pubid/iso/identifiers/technical_report.rb +103 -0
- data/lib/pubid/iso/identifiers/technical_specification.rb +102 -0
- data/lib/pubid/iso/identifiers/technology_trends_assessments.rb +95 -0
- data/lib/pubid/iso/identifiers.rb +32 -0
- data/lib/pubid/iso/normalizer.rb +89 -0
- data/lib/pubid/iso/parser.rb +534 -0
- data/lib/pubid/iso/rendering_style.rb +120 -0
- data/lib/pubid/iso/single_identifier.rb +60 -0
- data/lib/pubid/iso/supplement_identifier.rb +53 -0
- data/lib/pubid/iso/urn_generator.rb +313 -0
- data/lib/pubid/iso/urn_parser.rb +442 -0
- data/lib/pubid/iso/utilities.rb +86 -0
- data/lib/pubid/iso.rb +236 -0
- data/lib/pubid/itu/builder.rb +360 -0
- data/lib/pubid/itu/components/code.rb +84 -0
- data/lib/pubid/itu/components/designation.rb +35 -0
- data/lib/pubid/itu/components/sector.rb +46 -0
- data/lib/pubid/itu/components/series.rb +25 -0
- data/lib/pubid/itu/components.rb +12 -0
- data/lib/pubid/itu/i18n.rb +9 -0
- data/lib/pubid/itu/i18n.yaml +30 -0
- data/lib/pubid/itu/identifier.rb +4 -0
- data/lib/pubid/itu/identifiers/addendum.rb +15 -0
- data/lib/pubid/itu/identifiers/amendment.rb +19 -0
- data/lib/pubid/itu/identifiers/annex.rb +83 -0
- data/lib/pubid/itu/identifiers/annex_of_recommendation.rb +87 -0
- data/lib/pubid/itu/identifiers/appendix_of_recommendation.rb +92 -0
- data/lib/pubid/itu/identifiers/base.rb +482 -0
- data/lib/pubid/itu/identifiers/combined_identifier.rb +121 -0
- data/lib/pubid/itu/identifiers/corrigendum.rb +35 -0
- data/lib/pubid/itu/identifiers/errata.rb +15 -0
- data/lib/pubid/itu/identifiers/handbook.rb +38 -0
- data/lib/pubid/itu/identifiers/question.rb +63 -0
- data/lib/pubid/itu/identifiers/recommendation.rb +15 -0
- data/lib/pubid/itu/identifiers/report.rb +39 -0
- data/lib/pubid/itu/identifiers/special_publication.rb +31 -0
- data/lib/pubid/itu/identifiers/standard_serialization.rb +58 -0
- data/lib/pubid/itu/identifiers/supplement.rb +149 -0
- data/lib/pubid/itu/identifiers.rb +25 -0
- data/lib/pubid/itu/model.rb +111 -0
- data/lib/pubid/itu/parser.rb +679 -0
- data/lib/pubid/itu/urn_generator.rb +135 -0
- data/lib/pubid/itu/urn_parser.rb +31 -0
- data/lib/pubid/itu.rb +78 -0
- data/lib/pubid/jcgm/builder.rb +106 -0
- data/lib/pubid/jcgm/components/publisher.rb +20 -0
- data/lib/pubid/jcgm/components.rb +9 -0
- data/lib/pubid/jcgm/identifier.rb +21 -0
- data/lib/pubid/jcgm/identifiers/amendment.rb +24 -0
- data/lib/pubid/jcgm/identifiers/corrigendum.rb +34 -0
- data/lib/pubid/jcgm/identifiers/guide.rb +21 -0
- data/lib/pubid/jcgm/identifiers/gum_guide.rb +39 -0
- data/lib/pubid/jcgm/identifiers/meeting.rb +47 -0
- data/lib/pubid/jcgm/identifiers.rb +13 -0
- data/lib/pubid/jcgm/parser.rb +151 -0
- data/lib/pubid/jcgm/renderer.rb +89 -0
- data/lib/pubid/jcgm/single_identifier.rb +118 -0
- data/lib/pubid/jcgm/supplement_identifier.rb +51 -0
- data/lib/pubid/jcgm/urn_generator.rb +116 -0
- data/lib/pubid/jcgm/urn_parser.rb +177 -0
- data/lib/pubid/jcgm.rb +86 -0
- data/lib/pubid/jis/builder.rb +116 -0
- data/lib/pubid/jis/identifier.rb +194 -0
- data/lib/pubid/jis/identifiers/amendment.rb +16 -0
- data/lib/pubid/jis/identifiers/corrigendum.rb +16 -0
- data/lib/pubid/jis/identifiers/explanation.rb +22 -0
- data/lib/pubid/jis/identifiers/japanese_industrial_standard.rb +16 -0
- data/lib/pubid/jis/identifiers/standard.rb +28 -0
- data/lib/pubid/jis/identifiers/technical_report.rb +32 -0
- data/lib/pubid/jis/identifiers/technical_specification.rb +32 -0
- data/lib/pubid/jis/identifiers.rb +17 -0
- data/lib/pubid/jis/parser.rb +135 -0
- data/lib/pubid/jis/renderer.rb +69 -0
- data/lib/pubid/jis/single_identifier.rb +31 -0
- data/lib/pubid/jis/supplement_identifier.rb +50 -0
- data/lib/pubid/jis/urn_generator.rb +25 -0
- data/lib/pubid/jis/urn_parser.rb +23 -0
- data/lib/pubid/jis.rb +69 -0
- data/lib/pubid/lutaml/no_store_registration.rb +30 -0
- data/lib/pubid/nist/builder.rb +481 -0
- data/lib/pubid/nist/caster.rb +1234 -0
- data/lib/pubid/nist/circular_supplement_builder.rb +293 -0
- data/lib/pubid/nist/components/code.rb +27 -0
- data/lib/pubid/nist/components/edition.rb +134 -0
- data/lib/pubid/nist/components/issue_number.rb +28 -0
- data/lib/pubid/nist/components/part.rb +77 -0
- data/lib/pubid/nist/components/stage.rb +53 -0
- data/lib/pubid/nist/components/supplement.rb +191 -0
- data/lib/pubid/nist/components/translation.rb +42 -0
- data/lib/pubid/nist/components/update.rb +103 -0
- data/lib/pubid/nist/components/version.rb +35 -0
- data/lib/pubid/nist/components/volume.rb +32 -0
- data/lib/pubid/nist/components.rb +18 -0
- data/lib/pubid/nist/configuration.rb +87 -0
- data/lib/pubid/nist/identifier.rb +8 -0
- data/lib/pubid/nist/identifiers/base.rb +744 -0
- data/lib/pubid/nist/identifiers/circular.rb +80 -0
- data/lib/pubid/nist/identifiers/circular_supplement.rb +60 -0
- data/lib/pubid/nist/identifiers/commercial_standard.rb +42 -0
- data/lib/pubid/nist/identifiers/commercial_standard_emergency.rb +58 -0
- data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +69 -0
- data/lib/pubid/nist/identifiers/crpl_report.rb +138 -0
- data/lib/pubid/nist/identifiers/dated_document.rb +49 -0
- data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +95 -0
- data/lib/pubid/nist/identifiers/grant_contractor_report.rb +36 -0
- data/lib/pubid/nist/identifiers/handbook.rb +58 -0
- data/lib/pubid/nist/identifiers/internal_report.rb +64 -0
- data/lib/pubid/nist/identifiers/letter_circular.rb +46 -0
- data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +74 -0
- data/lib/pubid/nist/identifiers/monograph.rb +80 -0
- data/lib/pubid/nist/identifiers/ncstar.rb +41 -0
- data/lib/pubid/nist/identifiers/nsrds.rb +41 -0
- data/lib/pubid/nist/identifiers/owmwp.rb +35 -0
- data/lib/pubid/nist/identifiers/report.rb +77 -0
- data/lib/pubid/nist/identifiers/special_publication.rb +46 -0
- data/lib/pubid/nist/identifiers/technical_note.rb +91 -0
- data/lib/pubid/nist/identifiers.rb +33 -0
- data/lib/pubid/nist/parser.rb +752 -0
- data/lib/pubid/nist/parser_output_normalizer.rb +233 -0
- data/lib/pubid/nist/preprocessor.rb +443 -0
- data/lib/pubid/nist/renderer.rb +43 -0
- data/lib/pubid/nist/router.rb +149 -0
- data/lib/pubid/nist/series/base.rb +58 -0
- data/lib/pubid/nist/series/crpl.rb +13 -0
- data/lib/pubid/nist/series/fips.rb +14 -0
- data/lib/pubid/nist/series/ir.rb +57 -0
- data/lib/pubid/nist/series/letter_preserving.rb +15 -0
- data/lib/pubid/nist/series/mono.rb +19 -0
- data/lib/pubid/nist/series/ncstar.rb +20 -0
- data/lib/pubid/nist/series.rb +49 -0
- data/lib/pubid/nist/supplement_identifier.rb +84 -0
- data/lib/pubid/nist/urn_generator.rb +133 -0
- data/lib/pubid/nist/urn_parser.rb +67 -0
- data/lib/pubid/nist.rb +138 -0
- data/lib/pubid/oasis/builder.rb +79 -0
- data/lib/pubid/oasis/identifier.rb +160 -0
- data/lib/pubid/oasis/identifiers/standard.rb +29 -0
- data/lib/pubid/oasis/identifiers.rb +9 -0
- data/lib/pubid/oasis/parser.rb +34 -0
- data/lib/pubid/oasis/renderer.rb +29 -0
- data/lib/pubid/oasis/urn_generator.rb +25 -0
- data/lib/pubid/oasis/urn_parser.rb +22 -0
- data/lib/pubid/oasis.rb +79 -0
- data/lib/pubid/ogc/builder.rb +33 -0
- data/lib/pubid/ogc/identifier.rb +81 -0
- data/lib/pubid/ogc/identifiers/document.rb +26 -0
- data/lib/pubid/ogc/identifiers.rb +10 -0
- data/lib/pubid/ogc/parser.rb +40 -0
- data/lib/pubid/ogc/renderer.rb +33 -0
- data/lib/pubid/ogc/urn_generator.rb +14 -0
- data/lib/pubid/ogc/urn_parser.rb +22 -0
- data/lib/pubid/ogc.rb +71 -0
- data/lib/pubid/oiml/builder.rb +270 -0
- data/lib/pubid/oiml/components/code.rb +37 -0
- data/lib/pubid/oiml/components.rb +9 -0
- data/lib/pubid/oiml/identifier.rb +64 -0
- data/lib/pubid/oiml/identifiers/amendment.rb +13 -0
- data/lib/pubid/oiml/identifiers/annex.rb +29 -0
- data/lib/pubid/oiml/identifiers/basic_publication.rb +15 -0
- data/lib/pubid/oiml/identifiers/bulletin.rb +137 -0
- data/lib/pubid/oiml/identifiers/code_number.rb +90 -0
- data/lib/pubid/oiml/identifiers/document.rb +15 -0
- data/lib/pubid/oiml/identifiers/errata.rb +15 -0
- data/lib/pubid/oiml/identifiers/expert_report.rb +15 -0
- data/lib/pubid/oiml/identifiers/guide.rb +15 -0
- data/lib/pubid/oiml/identifiers/recommendation.rb +15 -0
- data/lib/pubid/oiml/identifiers/seminar_report.rb +15 -0
- data/lib/pubid/oiml/identifiers/vocabulary.rb +15 -0
- data/lib/pubid/oiml/identifiers.rb +20 -0
- data/lib/pubid/oiml/parser.rb +274 -0
- data/lib/pubid/oiml/renderer.rb +232 -0
- data/lib/pubid/oiml/single_identifier.rb +113 -0
- data/lib/pubid/oiml/supplement_identifier.rb +88 -0
- data/lib/pubid/oiml/urn_generator.rb +78 -0
- data/lib/pubid/oiml/urn_parser.rb +39 -0
- data/lib/pubid/oiml.rb +81 -0
- data/lib/pubid/omg/builder.rb +19 -0
- data/lib/pubid/omg/identifier.rb +47 -0
- data/lib/pubid/omg/identifiers/specification.rb +32 -0
- data/lib/pubid/omg/identifiers.rb +9 -0
- data/lib/pubid/omg/parser.rb +40 -0
- data/lib/pubid/omg/renderer.rb +19 -0
- data/lib/pubid/omg.rb +51 -0
- data/lib/pubid/parser/common_parse_methods.rb +13 -0
- data/lib/pubid/parser/common_parse_rules.rb +56 -0
- data/lib/pubid/parser/grammar.rb +74 -0
- data/lib/pubid/parser.rb +9 -0
- data/lib/pubid/parsers/base.rb +11 -0
- data/lib/pubid/parsers/mr_string.rb +206 -0
- data/lib/pubid/plateau/builder.rb +54 -0
- data/lib/pubid/plateau/identifier.rb +4 -0
- data/lib/pubid/plateau/identifiers/annex.rb +16 -0
- data/lib/pubid/plateau/identifiers/base.rb +55 -0
- data/lib/pubid/plateau/identifiers/handbook.rb +28 -0
- data/lib/pubid/plateau/identifiers/technical_report.rb +16 -0
- data/lib/pubid/plateau/identifiers.rb +11 -0
- data/lib/pubid/plateau/parser.rb +73 -0
- data/lib/pubid/plateau/renderer.rb +51 -0
- data/lib/pubid/plateau/supplement_identifier.rb +68 -0
- data/lib/pubid/plateau/urn_generator.rb +29 -0
- data/lib/pubid/plateau/urn_parser.rb +43 -0
- data/lib/pubid/plateau.rb +82 -0
- data/lib/pubid/prefixes_support.rb +51 -0
- data/lib/pubid/renderers/annotator.rb +233 -0
- data/lib/pubid/renderers/base.rb +66 -0
- data/lib/pubid/renderers/directives_renderer.rb +75 -0
- data/lib/pubid/renderers/guide_renderer.rb +26 -0
- data/lib/pubid/renderers/human_readable.rb +83 -0
- data/lib/pubid/renderers/iwa_renderer.rb +20 -0
- data/lib/pubid/renderers/mr_string.rb +64 -0
- data/lib/pubid/renderers/supplement_renderer.rb +36 -0
- data/lib/pubid/renderers/urn.rb +11 -0
- data/lib/pubid/renderers.rb +15 -0
- data/lib/pubid/rendering/context.rb +168 -0
- data/lib/pubid/rendering/date.rb +27 -0
- data/lib/pubid/rendering/language.rb +21 -0
- data/lib/pubid/rendering/numbering.rb +42 -0
- data/lib/pubid/rendering/publisher.rb +25 -0
- data/lib/pubid/rendering/stage.rb +38 -0
- data/lib/pubid/rendering/supplement.rb +46 -0
- data/lib/pubid/rendering.rb +13 -0
- data/lib/pubid/sae/builder.rb +32 -0
- data/lib/pubid/sae/components/code.rb +9 -0
- data/lib/pubid/sae/components/date.rb +27 -0
- data/lib/pubid/sae/components/type.rb +23 -0
- data/lib/pubid/sae/components.rb +11 -0
- data/lib/pubid/sae/identifier.rb +4 -0
- data/lib/pubid/sae/identifiers/base.rb +47 -0
- data/lib/pubid/sae/identifiers.rb +8 -0
- data/lib/pubid/sae/parser.rb +55 -0
- data/lib/pubid/sae/renderer.rb +36 -0
- data/lib/pubid/sae/urn_generator.rb +30 -0
- data/lib/pubid/sae/urn_parser.rb +36 -0
- data/lib/pubid/sae.rb +66 -0
- data/lib/pubid/tgpp/builder.rb +41 -0
- data/lib/pubid/tgpp/identifier.rb +107 -0
- data/lib/pubid/tgpp/identifiers/technical_report.rb +32 -0
- data/lib/pubid/tgpp/identifiers/technical_specification.rb +33 -0
- data/lib/pubid/tgpp/identifiers.rb +11 -0
- data/lib/pubid/tgpp/parser.rb +78 -0
- data/lib/pubid/tgpp/renderer.rb +52 -0
- data/lib/pubid/tgpp/urn_generator.rb +39 -0
- data/lib/pubid/tgpp/urn_parser.rb +30 -0
- data/lib/pubid/tgpp.rb +83 -0
- data/lib/pubid/type_resolver.rb +59 -0
- data/lib/pubid/un/builder.rb +42 -0
- data/lib/pubid/un/identifier.rb +40 -0
- data/lib/pubid/un/identifiers/document.rb +24 -0
- data/lib/pubid/un/identifiers.rb +9 -0
- data/lib/pubid/un/parser.rb +25 -0
- data/lib/pubid/un/renderer.rb +11 -0
- data/lib/pubid/un.rb +46 -0
- data/lib/pubid/urn_generator/base.rb +110 -0
- data/lib/pubid/urn_parser/base.rb +81 -0
- data/lib/pubid/urn_parser/errors.rb +16 -0
- data/lib/pubid/urn_parser.rb +14 -0
- data/lib/pubid/utils/string_normalizer.rb +196 -0
- data/lib/pubid/utils.rb +7 -0
- data/lib/pubid/version.rb +3 -1
- data/lib/pubid/w3c/builder.rb +55 -0
- data/lib/pubid/w3c/identifier.rb +130 -0
- data/lib/pubid/w3c/identifiers/candidate_recommendation.rb +31 -0
- data/lib/pubid/w3c/identifiers/candidate_recommendation_draft.rb +31 -0
- data/lib/pubid/w3c/identifiers/draft_note.rb +31 -0
- data/lib/pubid/w3c/identifiers/note.rb +30 -0
- data/lib/pubid/w3c/identifiers/obsolete_recommendation.rb +31 -0
- data/lib/pubid/w3c/identifiers/proposed_edited_recommendation.rb +31 -0
- data/lib/pubid/w3c/identifiers/proposed_recommendation.rb +31 -0
- data/lib/pubid/w3c/identifiers/recommendation.rb +31 -0
- data/lib/pubid/w3c/identifiers/standard.rb +12 -0
- data/lib/pubid/w3c/identifiers/superseded_recommendation.rb +31 -0
- data/lib/pubid/w3c/identifiers/working_draft.rb +30 -0
- data/lib/pubid/w3c/identifiers.rb +25 -0
- data/lib/pubid/w3c/parser.rb +43 -0
- data/lib/pubid/w3c/renderer.rb +35 -0
- data/lib/pubid/w3c/urn_generator.rb +18 -0
- data/lib/pubid/w3c/urn_parser.rb +36 -0
- data/lib/pubid/w3c.rb +67 -0
- data/lib/pubid/xsf/builder.rb +17 -0
- data/lib/pubid/xsf/identifier.rb +66 -0
- data/lib/pubid/xsf/identifiers/xep.rb +31 -0
- data/lib/pubid/xsf/identifiers.rb +9 -0
- data/lib/pubid/xsf/parser.rb +25 -0
- data/lib/pubid/xsf/renderer.rb +23 -0
- data/lib/pubid/xsf/urn_generator.rb +13 -0
- data/lib/pubid/xsf/urn_parser.rb +18 -0
- data/lib/pubid/xsf.rb +81 -0
- data/lib/pubid.rb +551 -13
- data/lib/tasks/docs.rake +37 -0
- data/lib/tasks/export.rake +39 -0
- data/lib/tasks/website-data.json +7546 -0
- metadata +875 -171
- data/lib/pubid/registry.rb +0 -30
|
@@ -0,0 +1,1118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
class Identifier < Lutaml::Model::Serializable
|
|
5
|
+
# Components that serialize as a bare scalar when they carry only their
|
|
6
|
+
# single significant field: attribute name => the key the scalar is emitted
|
|
7
|
+
# under. `date` is RENAMED to `year`, matching the flat shape ISO and IEC
|
|
8
|
+
# already emit through their own converters.
|
|
9
|
+
FLAT_SCALAR_COMPONENTS = {
|
|
10
|
+
edition: "edition",
|
|
11
|
+
date: "year",
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
# The one field each of those components degenerates to.
|
|
15
|
+
FLAT_SCALAR_FIELDS = {
|
|
16
|
+
edition: :number,
|
|
17
|
+
date: :year,
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
# The one field each degenerate component collapses to, keyed by the
|
|
21
|
+
# component CLASS rather than the attribute name — so a collection
|
|
22
|
+
# attribute (`languages`, `copublishers`) and a flavor subclass
|
|
23
|
+
# (Pubid::Iec::Components::Publisher) are both covered by one entry.
|
|
24
|
+
#
|
|
25
|
+
# `Components::Code` is deliberately absent. A String left in `number`
|
|
26
|
+
# renders and serializes correctly today, and coercing it would turn
|
|
27
|
+
# `to_hash` from {"number" => "1000"} into {"number" => {"value" => "1000"}}
|
|
28
|
+
# on the six flavors that still declare a Code (iso, iec, nist, csa, sae,
|
|
29
|
+
# ccsds) — an index wire-format change, and one the `number` retype tranches
|
|
30
|
+
# sequence to land last. The two entries here are the components that RAISE
|
|
31
|
+
# when a String reaches them, so coercing them can only repair.
|
|
32
|
+
DEGENERATE_COMPONENT_FIELDS = {
|
|
33
|
+
Components::Language => :code,
|
|
34
|
+
Components::Publisher => :body,
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
# Keys lutaml reads out of the attribute hash itself
|
|
38
|
+
# (Serializable#extract_register_id), so they are reserved rather than
|
|
39
|
+
# unknown and must survive the unknown-key check.
|
|
40
|
+
RESERVED_INIT_KEYS = %i[lutaml_register].freeze
|
|
41
|
+
|
|
42
|
+
# Scalar aliases accepted by the constructor for a component-valued
|
|
43
|
+
# attribute: alias => [target attribute, the component field it fills].
|
|
44
|
+
# `year` is the one the issue names — it is a READER over `date`, never an
|
|
45
|
+
# attribute, on every date-based flavor, so `new(number: "1000", year: 2023)`
|
|
46
|
+
# used to build an identifier with no year and say nothing.
|
|
47
|
+
SCALAR_ATTRIBUTE_ALIASES = {
|
|
48
|
+
year: [:date, :year],
|
|
49
|
+
month: [:date, :month],
|
|
50
|
+
day: [:date, :day],
|
|
51
|
+
}.freeze
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
def format_registry
|
|
56
|
+
@format_registry || superclass&.format_registry
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
attr_writer :format_registry
|
|
60
|
+
|
|
61
|
+
# Polymorphic deserialization shared by every flavor base. lutaml's
|
|
62
|
+
# `key_value` polymorphic_map reads `_type` only to VALIDATE; it does not
|
|
63
|
+
# re-instantiate the concrete subclass. So a base-class `from_hash` would
|
|
64
|
+
# return a bare base object and drop subtype-specific attributes (e.g. a
|
|
65
|
+
# supplement's base). Route by `_type` to the concrete class
|
|
66
|
+
# named in `polymorphic_type_map`, then let its inherited from_hash (this
|
|
67
|
+
# method again, where klass == self) fall through to lutaml's real work.
|
|
68
|
+
#
|
|
69
|
+
# This generalizes the per-flavor `*_TYPE_MAP` dispatch that ISO, JIS,
|
|
70
|
+
# IEC, CCSDS and IHO each hand-rolled — one implementation, inherited by
|
|
71
|
+
# every flavor base. Cross-flavor dispatch (a nested adopted identifier
|
|
72
|
+
# whose _type belongs to a different flavor) is delegated to
|
|
73
|
+
# {TypeResolver}, which knows about every registered flavor.
|
|
74
|
+
def from_hash(data, options = {})
|
|
75
|
+
klass = concrete_class_for(data)
|
|
76
|
+
return klass.from_hash(data, options) if klass && klass != self
|
|
77
|
+
|
|
78
|
+
super(inflate_scalar_components(data), options)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Accept the flat scalar form that {#to_hash} now emits, and the nested
|
|
82
|
+
# form every stored row was written with. lutaml casts a Hash into a
|
|
83
|
+
# component but passes a String straight through (Attribute#cast_element),
|
|
84
|
+
# so the flat form has to be re-inflated here or it would be assigned raw.
|
|
85
|
+
#
|
|
86
|
+
# Reading BOTH shapes is what keeps published relaton-data indexes
|
|
87
|
+
# deserializing across this change.
|
|
88
|
+
def inflate_scalar_components(data)
|
|
89
|
+
return data unless data.is_a?(::Hash)
|
|
90
|
+
|
|
91
|
+
FLAT_SCALAR_COMPONENTS.each_with_object(data.dup) do |(attr, key), acc|
|
|
92
|
+
inflate_scalar_component(acc, attr, key)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @api private
|
|
97
|
+
def inflate_scalar_component(data, attr_name, flat_key)
|
|
98
|
+
return unless component_attribute?(attr_name)
|
|
99
|
+
# A flavor with its OWN converter for the flat key (IEC's
|
|
100
|
+
# year_from_kv) already knows how to read it, and inflating would fight
|
|
101
|
+
# it. A plain `map "edition", to: :edition` is not such a converter —
|
|
102
|
+
# it hands the value to lutaml, which cannot cast a String into a
|
|
103
|
+
# component — so that one still needs inflating.
|
|
104
|
+
return if converted_hash_keys.include?(flat_key)
|
|
105
|
+
# Only when the flat key RENAMES the attribute (`date` -> `year`) can
|
|
106
|
+
# it collide with a different attribute. ~20 flavors model the edition
|
|
107
|
+
# as a real `year` attribute of their own (ASHRAE, ASME, CIE, CSA, JIS,
|
|
108
|
+
# OGC, IEEE …), and folding that into a `date` component silently drops
|
|
109
|
+
# the year on every round trip — the same collision
|
|
110
|
+
# `fold_scalar_aliases` guards against on the constructor path.
|
|
111
|
+
# `edition` keeps its own name, so it is never a collision.
|
|
112
|
+
return if flat_key != attr_name.to_s && declared_attribute?(flat_key)
|
|
113
|
+
|
|
114
|
+
key = data.key?(flat_key) ? flat_key : flat_key.to_sym
|
|
115
|
+
value = data[key]
|
|
116
|
+
return if value.nil? || value.is_a?(::Hash) || value.is_a?(::Array)
|
|
117
|
+
|
|
118
|
+
data.delete(key)
|
|
119
|
+
data[attr_name.to_s] =
|
|
120
|
+
{ FLAT_SCALAR_FIELDS.fetch(attr_name).to_s => value.to_s }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# True when +name+ is a declared attribute on this class, under either a
|
|
124
|
+
# Symbol or a String key.
|
|
125
|
+
def declared_attribute?(name)
|
|
126
|
+
attributes.key?(name.to_sym) || attributes.key?(name.to_s)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# True when +name+ is declared on this class as a component (a nested
|
|
130
|
+
# Serializable), rather than a plain scalar. The coercions are
|
|
131
|
+
# type-aware: ~16 classes declare `edition` as a plain :string and must
|
|
132
|
+
# keep the scalar they were given.
|
|
133
|
+
def component_attribute?(name)
|
|
134
|
+
attr = attributes[name] || attributes[name.to_s]
|
|
135
|
+
return false unless attr
|
|
136
|
+
|
|
137
|
+
type = attr.type
|
|
138
|
+
type.is_a?(Class) && type <= Lutaml::Model::Serializable
|
|
139
|
+
rescue StandardError
|
|
140
|
+
false
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Serialization keys this class reads through a converter of its own
|
|
144
|
+
# (a `key_value` rule declared with `with: {to:, from:}`). Those keys are
|
|
145
|
+
# already handled and must not be rewritten underneath the flavor.
|
|
146
|
+
#
|
|
147
|
+
# Empty for a flavor with no `key_value` block, which serializes
|
|
148
|
+
# attribute names directly.
|
|
149
|
+
def converted_hash_keys
|
|
150
|
+
@converted_hash_keys ||=
|
|
151
|
+
begin
|
|
152
|
+
rules = mappings[:hash]&.mappings || []
|
|
153
|
+
rules.reject { |rule| rule.custom_methods.nil? || rule.custom_methods.empty? }
|
|
154
|
+
.map { |rule| rule.name.to_s }
|
|
155
|
+
rescue StandardError
|
|
156
|
+
[]
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# The blessed constructor path (pubid#360 item 3).
|
|
161
|
+
#
|
|
162
|
+
# Two jobs, both of which lutaml declines to do:
|
|
163
|
+
#
|
|
164
|
+
# * coerce a scalar into the component the attribute declares, so
|
|
165
|
+
# `edition: 2` and `year: 2023` work as written;
|
|
166
|
+
# * refuse a key this class does not know, instead of dropping it.
|
|
167
|
+
#
|
|
168
|
+
# The refusal is the important half. lutaml reads only declared attribute
|
|
169
|
+
# names out of the hash (Serialize#initialize_attributes) and reports
|
|
170
|
+
# nothing about the rest, so `new(number: "1000", year: 2023)` returned an
|
|
171
|
+
# identifier with no year at all and no diagnostic — which is exactly how
|
|
172
|
+
# the issue was filed. A dropped key is a silent wrong answer; a raise is
|
|
173
|
+
# a loud one.
|
|
174
|
+
#
|
|
175
|
+
# Safe to apply here because neither internal rebuild path routes
|
|
176
|
+
# attribute keys through the constructor: `from_hash` calls `new` with no
|
|
177
|
+
# attributes and assigns through setters afterwards, and `#exclude`
|
|
178
|
+
# rebuilds from `self.class.attributes` — only declared names, by
|
|
179
|
+
# construction.
|
|
180
|
+
#
|
|
181
|
+
# @param attrs [Hash] caller-supplied attributes
|
|
182
|
+
# @return [Hash] the same attributes, coerced
|
|
183
|
+
# @raise [ArgumentError] naming every key this class does not declare
|
|
184
|
+
def normalize_init_attributes(attrs)
|
|
185
|
+
return attrs.dup unless attrs.is_a?(::Hash)
|
|
186
|
+
|
|
187
|
+
normalized = attrs.dup
|
|
188
|
+
fold_scalar_aliases(normalized)
|
|
189
|
+
coerce_component_scalars(normalized)
|
|
190
|
+
reject_unknown_keys(normalized)
|
|
191
|
+
normalized
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Fold `year:` / `month:` / `day:` into the `date` component. Only when
|
|
195
|
+
# the alias is not itself a declared attribute — ~20 flavors model the
|
|
196
|
+
# edition as a plain `year` attribute, and folding there would destroy it.
|
|
197
|
+
def fold_scalar_aliases(attrs)
|
|
198
|
+
SCALAR_ATTRIBUTE_ALIASES.each do |alias_name, (target, field)|
|
|
199
|
+
key = attrs.key?(alias_name) ? alias_name : alias_name.to_s
|
|
200
|
+
next unless attrs.key?(key)
|
|
201
|
+
next if attributes.key?(alias_name) || attributes.key?(alias_name.to_s)
|
|
202
|
+
next unless component_attribute?(target)
|
|
203
|
+
|
|
204
|
+
value = attrs.delete(key)
|
|
205
|
+
next if value.nil?
|
|
206
|
+
|
|
207
|
+
existing = attrs[target] || attrs[target.to_s] || {}
|
|
208
|
+
existing = component_to_hash(existing)
|
|
209
|
+
attrs.delete(target.to_s)
|
|
210
|
+
attrs[target] = existing.merge(field => value.to_s)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Wrap a scalar in the component its attribute declares. A flavor that
|
|
215
|
+
# declares the attribute as a plain :string keeps the scalar untouched.
|
|
216
|
+
def coerce_component_scalars(attrs)
|
|
217
|
+
FLAT_SCALAR_FIELDS.each do |attr_name, field|
|
|
218
|
+
key = attrs.key?(attr_name) ? attr_name : attr_name.to_s
|
|
219
|
+
next unless attrs.key?(key)
|
|
220
|
+
next unless component_attribute?(attr_name)
|
|
221
|
+
|
|
222
|
+
value = attrs[key]
|
|
223
|
+
next if value.nil? || value.is_a?(::Hash) ||
|
|
224
|
+
value.is_a?(Lutaml::Model::Serialize)
|
|
225
|
+
|
|
226
|
+
attrs[key] = { field => value.to_s }
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
coerce_degenerate_components(attrs)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Wrap a scalar in a degenerate component wherever the attribute declares
|
|
233
|
+
# one, INCLUDING a collection — the residue of pubid#360 item 3.
|
|
234
|
+
#
|
|
235
|
+
# lutaml casts a Hash element into the component but passes a String
|
|
236
|
+
# through untouched, so `languages: ["en"]` stored raw Strings and then
|
|
237
|
+
# raised in the renderer (`undefined method 'code'`) and in the URN
|
|
238
|
+
# generator. The Hash form `languages: [{code: "en"}]` already worked, so
|
|
239
|
+
# this is a coercion gap, not a lutaml limit.
|
|
240
|
+
def coerce_degenerate_components(attrs)
|
|
241
|
+
attrs.keys.each do |key|
|
|
242
|
+
next unless key.is_a?(::Symbol) || key.is_a?(::String)
|
|
243
|
+
|
|
244
|
+
field = degenerate_field_for(key)
|
|
245
|
+
next unless field
|
|
246
|
+
|
|
247
|
+
attrs[key] = wrap_degenerate(attrs[key], field)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# The degenerate field of the component +key+ declares, or nil when it
|
|
252
|
+
# declares none. A collection attribute reports its ELEMENT class, so one
|
|
253
|
+
# lookup covers both shapes; the `<=` test is what admits a flavor
|
|
254
|
+
# subclass such as Pubid::Iec::Components::Publisher.
|
|
255
|
+
def degenerate_field_for(key)
|
|
256
|
+
type = declared_component_type(key)
|
|
257
|
+
return nil unless type
|
|
258
|
+
|
|
259
|
+
DEGENERATE_COMPONENT_FIELDS.find { |klass, _| type <= klass }&.last
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# The component class +key+ declares, or nil when the attribute is
|
|
263
|
+
# absent or declared as a plain scalar.
|
|
264
|
+
def declared_component_type(key)
|
|
265
|
+
type = (attributes[key.to_sym] || attributes[key.to_s])&.type
|
|
266
|
+
type.is_a?(::Class) ? type : nil
|
|
267
|
+
rescue StandardError
|
|
268
|
+
nil
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# A scalar becomes `{field => scalar}` and an Array is mapped
|
|
272
|
+
# element-wise. nil, a Hash and an already-built component pass through
|
|
273
|
+
# untouched, so every shape that worked before still works.
|
|
274
|
+
def wrap_degenerate(value, field)
|
|
275
|
+
if value.is_a?(::Array)
|
|
276
|
+
return value.map { |v| wrap_degenerate(v, field) }
|
|
277
|
+
end
|
|
278
|
+
return value if value.nil? || value.is_a?(::Hash) ||
|
|
279
|
+
value.is_a?(Lutaml::Model::Serialize)
|
|
280
|
+
|
|
281
|
+
{ field => value.to_s }
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def reject_unknown_keys(attrs)
|
|
285
|
+
allowed = RESERVED_INIT_KEYS + extra_init_keys
|
|
286
|
+
unknown = attrs.keys.reject do |key|
|
|
287
|
+
# A key that is neither a Symbol nor a String cannot name an
|
|
288
|
+
# attribute, so it is unknown by definition — and `to_sym` on it
|
|
289
|
+
# would raise NoMethodError instead of reporting that.
|
|
290
|
+
next false unless key.is_a?(::Symbol) || key.is_a?(::String)
|
|
291
|
+
|
|
292
|
+
allowed.include?(key.to_sym) || declared_attribute?(key)
|
|
293
|
+
end
|
|
294
|
+
return if unknown.empty?
|
|
295
|
+
|
|
296
|
+
raise ArgumentError,
|
|
297
|
+
"unknown attribute#{'s' if unknown.size > 1} for " \
|
|
298
|
+
"#{name || self}: #{unknown.map(&:to_s).sort.join(', ')}"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Constructor parameters a flavor accepts that are not lutaml attributes.
|
|
302
|
+
# IEEE takes `code:` and `draft:` this way — runtime-only values its
|
|
303
|
+
# `initialize` turns into component objects. Override to widen.
|
|
304
|
+
#
|
|
305
|
+
# @return [Array<Symbol>]
|
|
306
|
+
def extra_init_keys
|
|
307
|
+
[]
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# @api private
|
|
311
|
+
def component_to_hash(value)
|
|
312
|
+
case value
|
|
313
|
+
when ::Hash then value.transform_keys(&:to_sym)
|
|
314
|
+
when Lutaml::Model::Serialize then value.to_hash.transform_keys(&:to_sym)
|
|
315
|
+
else {}
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# lutaml's nested polymorphic cast (Attribute#cast → apply_mappings)
|
|
320
|
+
# bypasses {from_hash}, so cross-flavor dispatch wouldn't fire for a
|
|
321
|
+
# nested attribute that carries another flavor's _type. Intercept
|
|
322
|
+
# apply_mappings and route to {from_hash} so the concrete class's own
|
|
323
|
+
# mappings (with their flavor-specific custom cast methods, e.g. ISO's
|
|
324
|
+
# number_from_kv) drive deserialization.
|
|
325
|
+
def apply_mappings(doc, format, options = {})
|
|
326
|
+
return super unless hash_with_type?(doc, format)
|
|
327
|
+
|
|
328
|
+
klass = concrete_class_for(doc)
|
|
329
|
+
return super unless klass && klass != self
|
|
330
|
+
|
|
331
|
+
klass.from_hash(doc, options)
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Resolve a polymorphic _type to the concrete class that owns it:
|
|
335
|
+
# this flavor's own map first, then any registered flavor's map via
|
|
336
|
+
# {TypeResolver}. Returns nil for blank or unknown types.
|
|
337
|
+
def concrete_class_for(data)
|
|
338
|
+
type = data && (data["_type"] || data[:_type])
|
|
339
|
+
return nil unless type
|
|
340
|
+
|
|
341
|
+
polymorphic_type_map[type] || ::Pubid::TypeResolver.resolve(type)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Map of polymorphic_name ("pubid:iso:corrigendum") => concrete class for
|
|
345
|
+
# the flavor this base belongs to. Built once by scanning the flavor's
|
|
346
|
+
# `Identifiers` namespace for Pubid::Identifier descendants and unioning
|
|
347
|
+
# the flavor's `identifier_types` registry (which may register classes
|
|
348
|
+
# living outside that namespace, e.g. ISO's BundledIdentifier). Memoized
|
|
349
|
+
# per class. Public so {TypeResolver} can read another flavor's map for
|
|
350
|
+
# cross-flavor polymorphic dispatch.
|
|
351
|
+
def polymorphic_type_map
|
|
352
|
+
@polymorphic_type_map ||=
|
|
353
|
+
identifier_registry_classes.each_with_object({}) do |klass, map|
|
|
354
|
+
poly = klass.polymorphic_name
|
|
355
|
+
map[poly] ||= klass if poly
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
private
|
|
360
|
+
|
|
361
|
+
def hash_with_type?(doc, format)
|
|
362
|
+
format == :hash && doc.is_a?(Hash) && (doc["_type"] || doc[:_type])
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
private
|
|
366
|
+
|
|
367
|
+
# The flavor module namespacing this base, e.g. Pubid::Iso for
|
|
368
|
+
# Pubid::Iso::Identifier (and for the inherited Pubid::Iso::Identifiers::
|
|
369
|
+
# Corrigendum). Always the first two name components, so it is stable
|
|
370
|
+
# whether `self` is the base or a concrete subclass.
|
|
371
|
+
def flavor_module
|
|
372
|
+
@flavor_module ||= Object.const_get(name.split("::")[0, 2].join("::"))
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def identifiers_namespace
|
|
376
|
+
@identifiers_namespace ||= flavor_module.const_get(:Identifiers)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def identifier_registry_classes
|
|
380
|
+
(scanned_identifier_classes +
|
|
381
|
+
registered_identifier_classes +
|
|
382
|
+
additional_identifier_classes +
|
|
383
|
+
own_base_class).uniq
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# The flavor's own Identifier base, when a builder instantiates it
|
|
387
|
+
# directly (BSI does, for the members of a bundled identifier). Such an
|
|
388
|
+
# object serializes with `_type: "pubid:<flavor>:identifier"`, and
|
|
389
|
+
# without an entry here that type resolves to nothing — so a nested
|
|
390
|
+
# polymorphic attribute typed ::Pubid::Identifier silently deserialized
|
|
391
|
+
# the member as the abstract root instead of the flavor class. Added
|
|
392
|
+
# LAST, so a concrete leaf that somehow claimed the same name still wins.
|
|
393
|
+
def own_base_class
|
|
394
|
+
return [] if self == ::Pubid::Identifier
|
|
395
|
+
return [] unless name && polymorphic_name
|
|
396
|
+
|
|
397
|
+
[self]
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# Concrete identifier classes found directly in the flavor's `Identifiers`
|
|
401
|
+
# namespace. Empty for the abstract root (no flavor) or a flavor without
|
|
402
|
+
# that namespace. Only this namespace is scanned (never the flavor top
|
|
403
|
+
# level), to avoid force-autoloading unrelated flavor constants.
|
|
404
|
+
def scanned_identifier_classes
|
|
405
|
+
return [] unless flavor_identifiers_namespace?
|
|
406
|
+
|
|
407
|
+
identifiers_namespace.constants.filter_map do |const|
|
|
408
|
+
klass = identifiers_namespace.const_get(const)
|
|
409
|
+
klass if klass.is_a?(Class) && klass < ::Pubid::Identifier
|
|
410
|
+
rescue NameError
|
|
411
|
+
nil
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# Classes the flavor explicitly registers via `identifier_types`, if any.
|
|
416
|
+
def registered_identifier_classes
|
|
417
|
+
return [] unless flavor_module.respond_to?(:identifier_types)
|
|
418
|
+
|
|
419
|
+
Array(flavor_module.identifier_types)
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Hook for identifier classes that carry a polymorphic `_type` but live
|
|
423
|
+
# OUTSIDE the flavor's `Identifiers` namespace (e.g. Pubid::Iso::
|
|
424
|
+
# BundledIdentifier, Pubid::Iec::SingleIdentifier). Flavors with such
|
|
425
|
+
# classes override this; the default is none. Replaces the hand-listing
|
|
426
|
+
# of these in the old static `*_TYPE_MAP`s.
|
|
427
|
+
def additional_identifier_classes
|
|
428
|
+
[]
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def flavor_identifiers_namespace?
|
|
432
|
+
flavor_module.const_defined?(:Identifiers)
|
|
433
|
+
rescue NameError
|
|
434
|
+
false
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
attribute :_type, :string, polymorphic_class: true
|
|
439
|
+
attribute :number, Components::Code
|
|
440
|
+
attribute :part, Components::Code
|
|
441
|
+
attribute :subpart, Components::Code
|
|
442
|
+
attribute :stage_iteration, Components::Iteration
|
|
443
|
+
attribute :date, Components::Date
|
|
444
|
+
attribute :edition, Components::Edition
|
|
445
|
+
attribute :languages, Components::Language, collection: true
|
|
446
|
+
attribute :publisher, Components::Publisher
|
|
447
|
+
attribute :copublishers, Components::Publisher, collection: true
|
|
448
|
+
attribute :type, Components::Type
|
|
449
|
+
attribute :stage, Components::Stage
|
|
450
|
+
attribute :locality, Components::Locality
|
|
451
|
+
attribute :typed_stage, Components::TypedStage
|
|
452
|
+
attribute :all_parts, Lutaml::Model::Type::Boolean, default: false
|
|
453
|
+
# base is declared by supplement subclasses with proper type
|
|
454
|
+
def base
|
|
455
|
+
nil
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# The underlying standard, with amendment / corrigendum / Expert-commentary
|
|
459
|
+
# / Flex-version wrappers peeled recursively. Wrapper subclasses override
|
|
460
|
+
# this; a plain identifier is its own base document.
|
|
461
|
+
def base_document
|
|
462
|
+
self
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# @return [String, nil] publication year from the date component
|
|
466
|
+
def year
|
|
467
|
+
date&.year&.to_s
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Canonicalize the serialized hash so it never carries a defaulted attribute
|
|
471
|
+
# still at its default (or empty) value. This makes to_hash a pure function
|
|
472
|
+
# of the identifier's values, independent of how the object was built.
|
|
473
|
+
#
|
|
474
|
+
# Motivation: lutaml materializes each attribute's default as an explicit
|
|
475
|
+
# assignment during deserialization (flipping using_default? to false), so a
|
|
476
|
+
# naive from_hash(x).to_hash re-emits defaults that parse(x).to_hash omits —
|
|
477
|
+
# breaking the exact-equality round-trip relaton-index relies on
|
|
478
|
+
# (from_hash(raw).to_hash == raw). pubid never consults lutaml's unset
|
|
479
|
+
# tracking and defines no render_default: true, so a defaulted attribute at
|
|
480
|
+
# its default/empty value carries no meaning and does not belong in the
|
|
481
|
+
# canonical hash. Dropping it here (rather than repairing from_hash) fixes
|
|
482
|
+
# the round-trip through every construction path — parse, from_hash, manual.
|
|
483
|
+
def to_hash(*args)
|
|
484
|
+
hash = super
|
|
485
|
+
canonicalize_hash(self, hash) if hash.is_a?(::Hash)
|
|
486
|
+
hash
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# Recursively drop attributes holding only their default (or empty) value
|
|
490
|
+
# from +hash+, the serialization of +model+. Recurses into nested component
|
|
491
|
+
# / identifier values because lutaml serializes those via its own transform
|
|
492
|
+
# — bypassing their public to_hash — so a nested defaulted attribute (e.g.
|
|
493
|
+
# Components::Supplement#has_revision) would otherwise leak into the parent
|
|
494
|
+
# hash and break the idempotent round-trip.
|
|
495
|
+
def canonicalize_hash(model, hash)
|
|
496
|
+
register = model.lutaml_register
|
|
497
|
+
model.class.attributes(register).each do |name, attr|
|
|
498
|
+
canonicalize_attr(model, hash, name, attr) unless name == :_type
|
|
499
|
+
end
|
|
500
|
+
flatten_scalar_components(model, hash)
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
# A component that carries only ONE meaningful value serializes as that
|
|
504
|
+
# value: `edition: "2"`, not `edition: {number: "2"}`; `year: "2024"`, not
|
|
505
|
+
# `date: {year: "2024"}`. That is the shape an index row wants to be read
|
|
506
|
+
# in, and the shape a caller can write without knowing pubid's component
|
|
507
|
+
# classes — the complaint in pubid#360 item 3.
|
|
508
|
+
#
|
|
509
|
+
# The test is per INSTANCE, not per flavor, because one flavor's corpus
|
|
510
|
+
# holds both shapes. Of 9,730 dated IEC identifiers, 9,728 carry a year
|
|
511
|
+
# alone but 10 carry a month (`IEC CA 01:2025-10`) and 2 are undated
|
|
512
|
+
# (`IEC 60050:--`); a flavor-wide switch would have discarded those.
|
|
513
|
+
#
|
|
514
|
+
# Flavors that already emit the flat form are untouched for free: IEC and
|
|
515
|
+
# ISO serialize the date as top-level year/month/day, so no "date" key ever
|
|
516
|
+
# reaches this method.
|
|
517
|
+
def flatten_scalar_components(model, hash)
|
|
518
|
+
FLAT_SCALAR_COMPONENTS.each do |attr_name, flat_key|
|
|
519
|
+
flatten_scalar_component(model, hash, attr_name, flat_key)
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
# Replace +hash+'s serialization of +attr_name+ with a bare scalar, when
|
|
524
|
+
# the component holds nothing but its single significant field.
|
|
525
|
+
def flatten_scalar_component(model, hash, attr_name, flat_key)
|
|
526
|
+
key = hash.key?(attr_name.to_s) ? attr_name.to_s : attr_name
|
|
527
|
+
return unless hash.key?(key) && hash[key].is_a?(::Hash)
|
|
528
|
+
return unless model.respond_to?(attr_name)
|
|
529
|
+
|
|
530
|
+
component = model.public_send(attr_name)
|
|
531
|
+
return unless component.is_a?(Lutaml::Model::Serialize)
|
|
532
|
+
|
|
533
|
+
scalar = degenerate_scalar(component, FLAT_SCALAR_FIELDS.fetch(attr_name))
|
|
534
|
+
return if scalar.nil?
|
|
535
|
+
# Never overwrite a key the flavor already emits under the flat name,
|
|
536
|
+
# and never claim a name the flavor declares as a real attribute of its
|
|
537
|
+
# own — ~20 flavors have a `year` attribute, and renaming `date` onto it
|
|
538
|
+
# would make the two indistinguishable on the way back in.
|
|
539
|
+
if flat_key != key.to_s
|
|
540
|
+
return if hash.key?(flat_key) || model.class.declared_attribute?(flat_key)
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
replace_key_in_place(hash, key, flat_key, scalar)
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# Replace +old_key+ with +new_key+ WITHOUT moving it to the end.
|
|
547
|
+
#
|
|
548
|
+
# A plain delete-then-assign appends, which would reorder every key after
|
|
549
|
+
# the one being flattened — cosmetic for `==`, but it rewrites the byte
|
|
550
|
+
# order of every generated relaton-data YAML row for no reason.
|
|
551
|
+
def replace_key_in_place(hash, old_key, new_key, value)
|
|
552
|
+
if old_key.to_s == new_key
|
|
553
|
+
hash[old_key] = value
|
|
554
|
+
return
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
rebuilt = hash.each_with_object({}) do |(k, v), acc|
|
|
558
|
+
if k == old_key
|
|
559
|
+
acc[new_key] = value
|
|
560
|
+
else
|
|
561
|
+
acc[k] = v
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
hash.replace(rebuilt)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# The scalar +component+ degenerates to, or nil when it carries anything
|
|
569
|
+
# besides +field+.
|
|
570
|
+
#
|
|
571
|
+
# A +field+ holding another component (ISO's edition number is a
|
|
572
|
+
# Components::Code) is deliberately NOT flattened: the flat form would come
|
|
573
|
+
# back as a plain String, so `from_hash(to_hash) == parse` would break —
|
|
574
|
+
# the silent inequality that CLAUDE.md records as the costliest failure
|
|
575
|
+
# mode in this codebase, because `#matches?` is built on `==`.
|
|
576
|
+
def degenerate_scalar(component, field)
|
|
577
|
+
register = component.lutaml_register
|
|
578
|
+
found = nil
|
|
579
|
+
|
|
580
|
+
component.class.attributes(register).each do |name, attr|
|
|
581
|
+
value = component.public_send(name)
|
|
582
|
+
# `Utils.empty?(nil)` is false, so nil needs its own test — an
|
|
583
|
+
# unpopulated attribute is exactly what makes a component degenerate.
|
|
584
|
+
next if value.nil? || Lutaml::Model::Utils.empty?(value)
|
|
585
|
+
next if attr.default_set?(register, component) &&
|
|
586
|
+
value == attr.default(register, component)
|
|
587
|
+
return nil unless name == field
|
|
588
|
+
return nil if value.is_a?(Lutaml::Model::Serialize)
|
|
589
|
+
|
|
590
|
+
found = value
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
found&.to_s
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# Canonicalize a single attribute against its serialized value in +hash+:
|
|
597
|
+
# drop it when it holds only its default/empty value, else recurse into it.
|
|
598
|
+
def canonicalize_attr(model, hash, name, attr)
|
|
599
|
+
key = hash.key?(name.to_s) ? name.to_s : name
|
|
600
|
+
return unless hash.key?(key)
|
|
601
|
+
|
|
602
|
+
value = model.public_send(name)
|
|
603
|
+
if default_valued?(value, attr, model)
|
|
604
|
+
hash.delete(key)
|
|
605
|
+
else
|
|
606
|
+
canonicalize_nested(value, hash[key])
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
# True when +value+ is +attr+'s default (or empty), so it can be dropped.
|
|
611
|
+
def default_valued?(value, attr, model)
|
|
612
|
+
register = model.lutaml_register
|
|
613
|
+
attr.default_set?(register, model) &&
|
|
614
|
+
(Lutaml::Model::Utils.empty?(value) ||
|
|
615
|
+
value == attr.default(register, model))
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
# Recurse into a nested component / identifier (or a collection of them) so
|
|
619
|
+
# its own defaulted attributes are canonicalized against its sub-hash.
|
|
620
|
+
def canonicalize_nested(value, sub)
|
|
621
|
+
if value.is_a?(Lutaml::Model::Serialize)
|
|
622
|
+
canonicalize_hash(value, sub) if sub.is_a?(::Hash)
|
|
623
|
+
elsif value.is_a?(::Array)
|
|
624
|
+
canonicalize_collection(value, sub)
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
# Canonicalize each element of a collection against its serialized sub-hash.
|
|
629
|
+
# Only zip when object and serialized sizes match, so a (never-observed)
|
|
630
|
+
# length mismatch can't pair an element with the wrong sub-hash — worst case
|
|
631
|
+
# it leaves that collection untouched.
|
|
632
|
+
def canonicalize_collection(models, subs)
|
|
633
|
+
return unless subs.is_a?(::Array) && models.size == subs.size
|
|
634
|
+
|
|
635
|
+
models.each_with_index do |model, i|
|
|
636
|
+
canonicalize_nested(model, subs[i])
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
private :canonicalize_hash, :canonicalize_attr, :default_valued?,
|
|
640
|
+
:canonicalize_nested, :canonicalize_collection,
|
|
641
|
+
:flatten_scalar_components, :flatten_scalar_component,
|
|
642
|
+
:degenerate_scalar, :replace_key_in_place
|
|
643
|
+
|
|
644
|
+
def initialize(attrs = {}, options = {})
|
|
645
|
+
attrs = self.class.normalize_init_attributes(attrs)
|
|
646
|
+
attrs[:_type] ||= self.class.polymorphic_name
|
|
647
|
+
super
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
def self.polymorphic_name
|
|
651
|
+
return nil unless name
|
|
652
|
+
|
|
653
|
+
parts = name.split("::")
|
|
654
|
+
flavor = parts[1]&.downcase
|
|
655
|
+
type_kebab = parts.last
|
|
656
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1-\2')
|
|
657
|
+
.gsub(/([a-z\d])([A-Z])/, '\1-\2')
|
|
658
|
+
.downcase
|
|
659
|
+
"pubid:#{flavor}:#{type_kebab}"
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def root
|
|
663
|
+
return base.root if base
|
|
664
|
+
|
|
665
|
+
self
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
# Unified render — delegates to format registry
|
|
669
|
+
def render(format: :human, **opts)
|
|
670
|
+
registry = self.class.format_registry
|
|
671
|
+
unless registry
|
|
672
|
+
raise ArgumentError, "No format registry configured on #{self.class}"
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
renderer = registry.renderer_for(format)
|
|
676
|
+
unless renderer
|
|
677
|
+
raise ArgumentError, "No renderer registered for format: #{format}"
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
# `:trademark` (IEEE) and `:with_volume` (ECMA) are render-time flags
|
|
681
|
+
# consumed by the renderer, not the rendering context — and some flavors
|
|
682
|
+
# override build_rendering_context with a strict signature, so strip them
|
|
683
|
+
# here rather than widening that signature.
|
|
684
|
+
ctx_opts = opts.except(:trademark, :with_volume)
|
|
685
|
+
context = build_rendering_context(renderer, format:, **ctx_opts)
|
|
686
|
+
render_opts = opts.slice(:with_edition, :trademark, :with_volume)
|
|
687
|
+
result = renderer.new(self).render(context:, **render_opts)
|
|
688
|
+
|
|
689
|
+
annotate_rendered(result, format, ctx_opts[:annotated], context)
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
# Apply semantic spans when the renderer did not.
|
|
693
|
+
#
|
|
694
|
+
# `context.annotated` reaches all 39 flavor renderers and all 39 ignore it,
|
|
695
|
+
# so annotation was a v1 -> v2 regression for every flavor but ISO. This is
|
|
696
|
+
# the single choke point every renderer already passes through, which is
|
|
697
|
+
# why the fallback lives here rather than in each renderer.
|
|
698
|
+
#
|
|
699
|
+
# The `<span` test is what keeps ISO's exact, render-time placement: a
|
|
700
|
+
# renderer that annotated itself is left alone.
|
|
701
|
+
def annotate_rendered(result, format, annotated, context)
|
|
702
|
+
return result unless annotated && format == :human
|
|
703
|
+
return result unless result.is_a?(String)
|
|
704
|
+
return result if result.include?("<span")
|
|
705
|
+
|
|
706
|
+
Renderers::Annotator.new(self, context).annotate(result)
|
|
707
|
+
end
|
|
708
|
+
private :annotate_rendered
|
|
709
|
+
|
|
710
|
+
# The same fallback, for a `to_s` that composes its string by hand instead
|
|
711
|
+
# of going through {#render} — ITU, CCSDS, CIE and CSA wrappers all do.
|
|
712
|
+
# Those never reach the choke point above, so they apply it on the way out.
|
|
713
|
+
#
|
|
714
|
+
# Every such `to_s` must also ACCEPT `annotated:`. Before they did, 13 type
|
|
715
|
+
# families raised on the flag (`wrong number of arguments (given 1,
|
|
716
|
+
# expected 0)` for the ones declaring no parameters at all), which is worse
|
|
717
|
+
# than a missing span: a wrapper is exactly the shape a consumer renders.
|
|
718
|
+
#
|
|
719
|
+
# This is deliberately a per-class opt-in rather than a module prepended to
|
|
720
|
+
# every identifier class. That was tried: prepending to ~500 ancestor
|
|
721
|
+
# chains made a GOST identical adoption render its adopted ISO Technical
|
|
722
|
+
# Report as `ISO TR …` instead of `ISO/TR …`, but only under full-suite
|
|
723
|
+
# load — the multi-flavor nondeterminism this file warns about elsewhere.
|
|
724
|
+
# An explicit call in the handful of classes that need it changes no
|
|
725
|
+
# ancestor chain and cannot have that effect.
|
|
726
|
+
#
|
|
727
|
+
# @param rendered [String] the plain rendering this method produced
|
|
728
|
+
# @param opts [Hash] the render options it was called with
|
|
729
|
+
# @return [String] annotated when `annotated:` was asked for, else as-is
|
|
730
|
+
def annotate_plain_render(rendered, **opts)
|
|
731
|
+
annotate_rendered(rendered, :human, opts[:annotated], nil)
|
|
732
|
+
end
|
|
733
|
+
protected :annotate_plain_render
|
|
734
|
+
|
|
735
|
+
# The tokens {Renderers::Annotator} looks for in this identifier's rendered
|
|
736
|
+
# string, as [attribute_name, css_class] pairs.
|
|
737
|
+
#
|
|
738
|
+
# The shared table names the components a printed identifier usually
|
|
739
|
+
# carries — publisher, type, number, part, year. A flavor that keeps its
|
|
740
|
+
# identity somewhere else answers with its own names appended: IEC's
|
|
741
|
+
# working documents are `technical_committee`/`wd_number`/`wd_stage`, and
|
|
742
|
+
# CSA's electrical code prints `cec_part`/`no_number` rather than the
|
|
743
|
+
# `number` it stores. Without this they carried no annotatable token at all.
|
|
744
|
+
#
|
|
745
|
+
# Appending rather than replacing keeps whatever the shared table does
|
|
746
|
+
# find; a token whose value is not in the string is skipped anyway.
|
|
747
|
+
def annotation_tokens
|
|
748
|
+
Renderers::Annotator::TOKENS
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
def to_s(**opts)
|
|
752
|
+
render(format: :human, **opts)
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
def to_mr_string
|
|
756
|
+
render(format: :mr_string)
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
# Filesystem-/URL-safe slug derived from the MR string. Defaults to
|
|
760
|
+
# `to_mr_string` because every flavor except NIST already emits an
|
|
761
|
+
# all-lowercase, filename-safe MR (only `[a-z0-9.-]` characters, plus
|
|
762
|
+
# `_` as the supplement separator and `-` as the copublisher separator).
|
|
763
|
+
# Flavors whose MR is not slug-safe (notably NIST, whose MR format is
|
|
764
|
+
# fixed by the pubid standard) override this to project the MR into a
|
|
765
|
+
# slug-safe form.
|
|
766
|
+
def to_slug
|
|
767
|
+
to_mr_string
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
# MR string template methods — flavors override as needed.
|
|
771
|
+
#
|
|
772
|
+
# The MR format is a lossless, dot-separated, all-lowercase, filename-safe
|
|
773
|
+
# slug mirroring `to_s`'s structure:
|
|
774
|
+
#
|
|
775
|
+
# {publisher}[-{copublisher}…][.{type}].{number}[-{part}[-{subpart}…]]
|
|
776
|
+
# [.{year}[-{month}[-{day}]]|’--’}][.{edition}][.{language}-{language}…]
|
|
777
|
+
# [.all-parts][_supplements…]
|
|
778
|
+
#
|
|
779
|
+
# Supplements append `_{type}.{number}.{year}` recursively, so a chained
|
|
780
|
+
# supplement like `…/Amd 3:2016/Cor 1:2017` round-trips as
|
|
781
|
+
# `…_amd.3.2016_cor.1.2017`. Copublishers join with `-`
|
|
782
|
+
# (`iso-iec.17031-1.2020`) so the slug never contains a path separator.
|
|
783
|
+
# Distinct identifiers never collide on `to_mr_string` (issue #142).
|
|
784
|
+
def mr_publisher
|
|
785
|
+
publisher&.to_s&.downcase&.tr("/", "-")
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
def mr_type
|
|
789
|
+
return nil unless typed_stage
|
|
790
|
+
|
|
791
|
+
code = typed_stage.type_code
|
|
792
|
+
return nil if code.nil? || code.empty? || code.to_s == "is"
|
|
793
|
+
|
|
794
|
+
code.to_s.downcase
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
def mr_number_with_part
|
|
798
|
+
num = mr_number
|
|
799
|
+
return nil unless num
|
|
800
|
+
|
|
801
|
+
segments = [num]
|
|
802
|
+
segments << mr_part if part
|
|
803
|
+
segments << mr_subpart if subpart
|
|
804
|
+
segments.compact.join("-").downcase
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
# Join MR segments and return nil rather than "" when nothing survives.
|
|
808
|
+
#
|
|
809
|
+
# Renderers::MrString does `parts.compact.join(".")`, and `compact` removes
|
|
810
|
+
# nil but NOT an empty string — so a hook that returns "" contributes a
|
|
811
|
+
# blank segment and the slug gains a double dot. The base
|
|
812
|
+
# #mr_number_with_part guards this with its own `return nil unless num`;
|
|
813
|
+
# flavor overrides that append their own marker to `super` need the same
|
|
814
|
+
# guard, and this is it.
|
|
815
|
+
def mr_join(*segments)
|
|
816
|
+
joined = segments.compact.reject { |s| s.to_s.empty? }.join("-")
|
|
817
|
+
joined.empty? ? nil : joined
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
def mr_number
|
|
821
|
+
number&.to_s&.downcase
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
def mr_part
|
|
825
|
+
part&.to_s&.downcase
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
def mr_subpart
|
|
829
|
+
subpart&.to_s&.downcase
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
def mr_year
|
|
833
|
+
return nil unless date
|
|
834
|
+
return "--" if date.respond_to?(:undated?) && date.undated?
|
|
835
|
+
return nil unless date.year
|
|
836
|
+
|
|
837
|
+
result = date.year.to_s
|
|
838
|
+
result += "-#{date.month}" if date.respond_to?(:month) && date.month
|
|
839
|
+
result += "-#{date.day}" if date.respond_to?(:day) && date.day
|
|
840
|
+
result
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
def mr_edition
|
|
844
|
+
return nil unless edition&.number
|
|
845
|
+
|
|
846
|
+
"ed#{edition.number}"
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
# Hook for flavors whose identity includes a volume discriminator — ECMA-269
|
|
850
|
+
# ed3 vol1..vol4 are four distinct documents sharing one docidentifier.
|
|
851
|
+
# Literally nil here, NOT `volume&.to_s`: Pubid::Nist::Identifiers::Base
|
|
852
|
+
# declares its own `volume` attribute with different MR semantics, so a
|
|
853
|
+
# generic reader would change NIST's slug. A flavor that needs the segment
|
|
854
|
+
# overrides this (see Pubid::Ecma::Identifier#mr_volume).
|
|
855
|
+
def mr_volume
|
|
856
|
+
nil
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
def mr_languages
|
|
860
|
+
return nil unless languages&.any?
|
|
861
|
+
|
|
862
|
+
# Hyphen-joined, no parens — parens are shell-/URL-unsafe.
|
|
863
|
+
languages.map { |l| l.code.to_s.downcase }.join("-")
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
def mr_all_parts
|
|
867
|
+
return nil unless all_parts
|
|
868
|
+
|
|
869
|
+
"all-parts"
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
# Hook: supplement / wrapper subclasses return the `{type}.{number}.{year}`
|
|
873
|
+
# suffix that distinguishes them from their base. `nil` for non-supplements.
|
|
874
|
+
# The base MrString renderer recurses into `base` whenever this returns
|
|
875
|
+
# a non-nil value, so chained supplements (Cor → Amd → IS) render fully.
|
|
876
|
+
# The suffix is appended with `_` (not `/`) so the MR stays filename-safe.
|
|
877
|
+
def mr_supplement_suffix
|
|
878
|
+
nil
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
# URN template methods — flavors override as needed
|
|
882
|
+
def urn_type_code
|
|
883
|
+
nil
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
def urn_supplement_type
|
|
887
|
+
nil
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
# Supplement rendering hook — flavors override for supplement-specific rendering
|
|
891
|
+
def to_supplement_s(**opts)
|
|
892
|
+
to_s(**opts)
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
# Default URN generation — resolves flavor's UrnGenerator class
|
|
896
|
+
def to_urn
|
|
897
|
+
resolve_urn_generator.new(self).generate
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
def resolve_urn_generator
|
|
901
|
+
flavor = self.class.name.split("::")[1]
|
|
902
|
+
Object.const_get("Pubid::#{flavor}::UrnGenerator")
|
|
903
|
+
rescue NameError
|
|
904
|
+
Pubid::UrnGenerator::Base
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
# Excluded attributes are nilled; every other value is passed through
|
|
908
|
+
# #exclude_from_nested so the exclusion also propagates into nested
|
|
909
|
+
# identifiers — wrapper types (adopted standards, consolidated amendments,
|
|
910
|
+
# expert-commentary wrappers) delegate their date to an inner identifier
|
|
911
|
+
# rather than storing it in their own attribute.
|
|
912
|
+
def exclude(*args)
|
|
913
|
+
# :amendment / :supplement are structural, not attributes — they reduce
|
|
914
|
+
# a supplemented identifier to the standard it wraps (#drop_supplements).
|
|
915
|
+
supplement_keys = args & %i[amendment supplement]
|
|
916
|
+
unless supplement_keys.empty?
|
|
917
|
+
return drop_supplements.exclude(*(args - supplement_keys))
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
excluded_args = args.dup
|
|
921
|
+
# Map :year to :date (year-in-date flavors), but ALSO keep :year so a
|
|
922
|
+
# flavor that models the edition as a plain `year` attribute (e.g. GOST)
|
|
923
|
+
# has it excluded too. The loop below nils whichever name the flavor has.
|
|
924
|
+
excluded_args << :date if excluded_args.include?(:year)
|
|
925
|
+
|
|
926
|
+
attrs = self.class.attributes.each_with_object({}) do |(name, _), h|
|
|
927
|
+
value = excluded_args.include?(name) ? nil : public_send(name)
|
|
928
|
+
h[name] = exclude_from_nested(value, args)
|
|
929
|
+
end
|
|
930
|
+
# Splat the rebuilt attributes as keywords. Flavors whose identifier
|
|
931
|
+
# #initialize is keyword-only (ITU, IEEE, …) would raise ArgumentError on
|
|
932
|
+
# a positional hash under Ruby 3 kwarg separation; the base
|
|
933
|
+
# initialize(attrs = {}, options = {}) still accepts **attrs because Ruby
|
|
934
|
+
# passes keywords to a no-keyword method as a trailing positional Hash.
|
|
935
|
+
self.class.new(**attrs)
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
# The standard this identifier supplements, dropping its own supplement
|
|
939
|
+
# layer (one level). Overridden by ConsolidatedIdentifier / Amendment /
|
|
940
|
+
# Corrigendum; a non-supplement identifier supplements nothing, so it is
|
|
941
|
+
# returned unchanged.
|
|
942
|
+
def drop_supplements
|
|
943
|
+
self
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
# Fuzz-level equality: two identifiers match when they are equal after
|
|
947
|
+
# excluding the given aspects. `ignore` accepts the same symbols as #exclude
|
|
948
|
+
# (e.g. :date, :edition, :amendment). This is the primitive relaton uses to
|
|
949
|
+
# match a reference against catalogue hits at varying strictness.
|
|
950
|
+
def matches?(other, ignore: [])
|
|
951
|
+
return false unless other.is_a?(::Pubid::Identifier)
|
|
952
|
+
|
|
953
|
+
exclude(*ignore) == other.exclude(*ignore)
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
def new_edition_of?(other)
|
|
957
|
+
unless publisher == other.publisher
|
|
958
|
+
raise ArgumentError,
|
|
959
|
+
"Cannot compare edition: different publisher"
|
|
960
|
+
end
|
|
961
|
+
unless number == other.number
|
|
962
|
+
raise ArgumentError,
|
|
963
|
+
"Cannot compare edition: different number"
|
|
964
|
+
end
|
|
965
|
+
unless part == other.part
|
|
966
|
+
raise ArgumentError,
|
|
967
|
+
"Cannot compare edition: different part"
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
unless date && other.date
|
|
971
|
+
raise ArgumentError,
|
|
972
|
+
"Cannot compare identifier without date/year"
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
return date.year > other.date.year if date.year != other.date.year
|
|
976
|
+
|
|
977
|
+
if edition && other.edition
|
|
978
|
+
return edition.number > other.edition.number
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
false
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
# --- Relational algebra (pubid/pubid#247) ---
|
|
985
|
+
# Each predicate returns true / false. Predicates that don't apply to
|
|
986
|
+
# this identifier type (e.g. supplement checks on a non-wrapper) return
|
|
987
|
+
# false rather than raising. This makes them safe for chain-of-checks
|
|
988
|
+
# patterns like `id.related_to?(other)`.
|
|
989
|
+
|
|
990
|
+
# Self is a supplement (amendment / corrigendum / errata / supplement)
|
|
991
|
+
# of +other+. Self must be a wrapper (has a +base+ attribute) whose
|
|
992
|
+
# inner base equals +other+.
|
|
993
|
+
def supplement_of?(other)
|
|
994
|
+
return false unless other.is_a?(::Pubid::Identifier)
|
|
995
|
+
return false unless self.class.attributes.key?(:base)
|
|
996
|
+
|
|
997
|
+
base == other
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
# Inverse of #supplement_of? — self is the base document and +other+
|
|
1001
|
+
# is a supplement attached to it.
|
|
1002
|
+
def has_supplement?(other)
|
|
1003
|
+
other.is_a?(::Pubid::Identifier) && other.supplement_of?(self)
|
|
1004
|
+
end
|
|
1005
|
+
|
|
1006
|
+
# Self and +other+ refer to the same document but differ only in the
|
|
1007
|
+
# trailing date / year — the "dated vs undated" reference pattern.
|
|
1008
|
+
def dated_version_of?(other)
|
|
1009
|
+
return false unless other.is_a?(::Pubid::Identifier)
|
|
1010
|
+
|
|
1011
|
+
matches?(other, ignore: [:date, :year]) && self != other
|
|
1012
|
+
end
|
|
1013
|
+
|
|
1014
|
+
# Self and +other+ have the same publisher + number but differ in
|
|
1015
|
+
# part, subpart, or edition. This is the "sibling documents" pattern:
|
|
1016
|
+
# same base standard, different sub-component.
|
|
1017
|
+
def sibling_of?(other)
|
|
1018
|
+
return false unless other.is_a?(::Pubid::Identifier)
|
|
1019
|
+
|
|
1020
|
+
publisher == other.publisher &&
|
|
1021
|
+
number == other.number &&
|
|
1022
|
+
self != other
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
# Self is an all-parts collection that covers +other+.
|
|
1026
|
+
def includes?(other)
|
|
1027
|
+
return false unless other.is_a?(::Pubid::Identifier)
|
|
1028
|
+
return false unless all_parts
|
|
1029
|
+
|
|
1030
|
+
root == other.root
|
|
1031
|
+
end
|
|
1032
|
+
|
|
1033
|
+
# Self is a draft version of +other+ (the published identifier).
|
|
1034
|
+
# IEEE uses draft_status + draft; ISO/IEC use typed_stage.
|
|
1035
|
+
def draft_of?(other)
|
|
1036
|
+
return false unless other.is_a?(::Pubid::Identifier)
|
|
1037
|
+
|
|
1038
|
+
root == other.root &&
|
|
1039
|
+
self != other &&
|
|
1040
|
+
matches?(other, ignore: [:date, :year, :stage, :typed_stage])
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
# Self and +other+ have the same publisher + number + part but differ
|
|
1044
|
+
# in edition / revision marker.
|
|
1045
|
+
def edition_of?(other)
|
|
1046
|
+
return false unless other.is_a?(::Pubid::Identifier)
|
|
1047
|
+
|
|
1048
|
+
publisher == other.publisher &&
|
|
1049
|
+
number == other.number &&
|
|
1050
|
+
part == other.part &&
|
|
1051
|
+
self != other
|
|
1052
|
+
end
|
|
1053
|
+
|
|
1054
|
+
# Any relational predicate holds.
|
|
1055
|
+
def related_to?(other)
|
|
1056
|
+
%i[supplement_of? has_supplement? dated_version_of? edition_of?
|
|
1057
|
+
sibling_of? includes? draft_of?].any? do |m|
|
|
1058
|
+
public_send(m, other)
|
|
1059
|
+
end
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
def hash
|
|
1063
|
+
@hash ||= compute_hash
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
def eql?(other)
|
|
1067
|
+
return false unless other.is_a?(self.class)
|
|
1068
|
+
|
|
1069
|
+
hash == other.hash && self == other
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
private
|
|
1073
|
+
|
|
1074
|
+
# Propagate an #exclude into a nested attribute value: recurse when it is
|
|
1075
|
+
# (or contains) another identifier, otherwise return it unchanged.
|
|
1076
|
+
# Components and scalars are copied as-is. Passes the original args so
|
|
1077
|
+
# nested identifiers re-apply the same :year->:date mapping themselves.
|
|
1078
|
+
def exclude_from_nested(value, args)
|
|
1079
|
+
case value
|
|
1080
|
+
when ::Pubid::Identifier
|
|
1081
|
+
value.exclude(*args)
|
|
1082
|
+
when Array
|
|
1083
|
+
value.map { |item| exclude_from_nested(item, args) }
|
|
1084
|
+
else
|
|
1085
|
+
value
|
|
1086
|
+
end
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
def build_rendering_context(_renderer, format:, with_edition: false,
|
|
1090
|
+
lang: :en, lang_single: false,
|
|
1091
|
+
stage_format_long: nil, with_date: nil,
|
|
1092
|
+
annotated: false)
|
|
1093
|
+
if format == :mr_string
|
|
1094
|
+
nil
|
|
1095
|
+
else
|
|
1096
|
+
Rendering::RenderingContext.new(
|
|
1097
|
+
with_language_code: lang_single ? :single : :none,
|
|
1098
|
+
stage_format_long: stage_format_long || false,
|
|
1099
|
+
with_date: with_date.nil? || with_date,
|
|
1100
|
+
annotated: annotated,
|
|
1101
|
+
)
|
|
1102
|
+
end
|
|
1103
|
+
end
|
|
1104
|
+
|
|
1105
|
+
def compute_hash
|
|
1106
|
+
attrs = [
|
|
1107
|
+
publisher,
|
|
1108
|
+
number,
|
|
1109
|
+
part,
|
|
1110
|
+
subpart,
|
|
1111
|
+
date,
|
|
1112
|
+
type,
|
|
1113
|
+
stage,
|
|
1114
|
+
]
|
|
1115
|
+
attrs.compact.map(&:hash).hash
|
|
1116
|
+
end
|
|
1117
|
+
end
|
|
1118
|
+
end
|