iev 0.3.0 → 0.3.3
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/.github/workflows/rake.yml +3 -20
- data/.github/workflows/release.yml +25 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +0 -2
- data/README.adoc +4 -4
- data/exe/iev-glossarist +21 -0
- data/iev.gemspec +12 -3
- data/lib/iev/cli/command.rb +109 -0
- data/lib/iev/cli/command_helper.rb +83 -0
- data/lib/iev/cli/ui.rb +70 -0
- data/lib/iev/cli.rb +22 -0
- data/lib/iev/converter/mathml_to_asciimath.rb +197 -0
- data/lib/iev/converter.rb +9 -0
- data/lib/iev/data_conversions.rb +39 -0
- data/lib/iev/db.rb +3 -3
- data/lib/iev/db_cache.rb +2 -2
- data/lib/iev/db_writer.rb +81 -0
- data/lib/iev/iso_639_2.yaml +4075 -0
- data/lib/iev/iso_639_code.rb +47 -0
- data/lib/iev/profiler.rb +69 -0
- data/lib/iev/relaton_db.rb +63 -0
- data/lib/iev/source_parser.rb +350 -0
- data/lib/iev/supersession_parser.rb +70 -0
- data/lib/iev/term_attrs_parser.rb +143 -0
- data/lib/iev/term_builder.rb +313 -0
- data/lib/iev/utilities.rb +58 -0
- data/lib/iev/version.rb +2 -2
- data/lib/iev.rb +24 -2
- metadata +153 -10
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: a5909046a19d741d4242dd6823366923efb0607de3ebf89fdcb99465310b6150
         | 
| 4 | 
            +
              data.tar.gz: 55812547af56e58d9ea68a1cc7413632558c51a8d3b0d22b48ee61e81afef390
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 6e5cca48906d1f65292cc58887b89564ae293da6cd081e47e70495d11f43c606b6db11d7d99db92210909b4fc3ccfcb54ffdb003191c17134f15c921cbded6c0
         | 
| 7 | 
            +
              data.tar.gz: 02f500f0bf2c1da875402a5f26f709d86ee7a4924fd4dd61796e653e260cd52d6c88fc37c9403bf4db479bdeb59664bc2cd599eaa00f40012ca0893a35a96164
         | 
    
        data/.github/workflows/rake.yml
    CHANGED
    
    | @@ -10,23 +10,6 @@ on: | |
| 10 10 |  | 
| 11 11 | 
             
            jobs:
         | 
| 12 12 | 
             
              rake:
         | 
| 13 | 
            -
                 | 
| 14 | 
            -
                 | 
| 15 | 
            -
             | 
| 16 | 
            -
                strategy:
         | 
| 17 | 
            -
                  fail-fast: false
         | 
| 18 | 
            -
                  matrix:
         | 
| 19 | 
            -
                    ruby: [ '3.0', '2.7', '2.6', '2.5' ]
         | 
| 20 | 
            -
                    os: [ ubuntu-latest, windows-latest, macos-latest ]
         | 
| 21 | 
            -
                    experimental: [ false ]
         | 
| 22 | 
            -
                steps:
         | 
| 23 | 
            -
                  - uses: actions/checkout@v2
         | 
| 24 | 
            -
                    with:
         | 
| 25 | 
            -
                      submodules: true
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                  - uses: ruby/setup-ruby@v1
         | 
| 28 | 
            -
                    with:
         | 
| 29 | 
            -
                      ruby-version: ${{ matrix.ruby }}
         | 
| 30 | 
            -
                      bundler-cache: true
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  - run: bundle exec rake
         | 
| 13 | 
            +
                uses: metanorma/ci/.github/workflows/generic-rake.yml@main
         | 
| 14 | 
            +
                secrets:
         | 
| 15 | 
            +
                  pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            # Auto-generated by Cimas: Do not edit it manually!
         | 
| 2 | 
            +
            # See https://github.com/metanorma/cimas
         | 
| 3 | 
            +
            name: release
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            on:
         | 
| 6 | 
            +
              workflow_dispatch:
         | 
| 7 | 
            +
                inputs:
         | 
| 8 | 
            +
                  next_version:
         | 
| 9 | 
            +
                    description: |
         | 
| 10 | 
            +
                      Next release version. Possible values: x.y.z, major, minor, patch (or pre|rc|etc).
         | 
| 11 | 
            +
                      Also, you can pass 'skip' to skip 'git tag' and do 'gem push' for the current version
         | 
| 12 | 
            +
                    required: true
         | 
| 13 | 
            +
                    default: 'skip'
         | 
| 14 | 
            +
              repository_dispatch:
         | 
| 15 | 
            +
                types: [ do-release ]
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            jobs:
         | 
| 18 | 
            +
              release:
         | 
| 19 | 
            +
                uses: metanorma/ci/.github/workflows/rubygems-release.yml@main
         | 
| 20 | 
            +
                with:
         | 
| 21 | 
            +
                  next_version: ${{ github.event.inputs.next_version }}
         | 
| 22 | 
            +
                secrets:
         | 
| 23 | 
            +
                  rubygems-api-key: ${{ secrets.METANORMA_CI_RUBYGEMS_API_KEY }}
         | 
| 24 | 
            +
                  pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
         | 
| 25 | 
            +
             | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/README.adoc
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            =  | 
| 1 | 
            +
            = IEV
         | 
| 2 2 |  | 
| 3 3 | 
             
            image:https://img.shields.io/gem/v/iev.svg["Gem Version", link="https://rubygems.org/gems/iev"]
         | 
| 4 4 | 
             
            image:https://github.com/metanorma/iev/workflows/rake/badge.svg["Build Status", link="https://github.com/metanorma/iev/actions?query=workflow%3Arake"]
         | 
| @@ -31,15 +31,15 @@ Or install it yourself as: | |
| 31 31 | 
             
            ----
         | 
| 32 32 | 
             
            # Get term
         | 
| 33 33 |  | 
| 34 | 
            -
             | 
| 34 | 
            +
            IEV.get("103-01-02", "en")
         | 
| 35 35 | 
             
            => "functional"
         | 
| 36 36 |  | 
| 37 37 | 
             
            # If code not found
         | 
| 38 | 
            -
             | 
| 38 | 
            +
            IEV.get("111-11-11", "en")
         | 
| 39 39 | 
             
            => ""
         | 
| 40 40 |  | 
| 41 41 | 
             
            # If language not found
         | 
| 42 | 
            -
             | 
| 42 | 
            +
            IEV.get("103-01-02", "eee")
         | 
| 43 43 | 
             
            => nil
         | 
| 44 44 | 
             
            ----
         | 
| 45 45 |  | 
    
        data/exe/iev-glossarist
    ADDED
    
    | @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            # encoding: UTF-8
         | 
| 3 | 
            +
            #
         | 
| 4 | 
            +
            # resolve bin path, ignoring symlinks
         | 
| 5 | 
            +
            require "pathname"
         | 
| 6 | 
            +
            bin_file = Pathname.new(__FILE__).realpath
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # add self to libpath
         | 
| 9 | 
            +
            $:.unshift File.expand_path("../../lib", bin_file)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            # Fixes https://github.com/rubygems/rubygems/issues/1420
         | 
| 12 | 
            +
            require "rubygems/specification"
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            class Gem::Specification
         | 
| 15 | 
            +
              def this; self; end
         | 
| 16 | 
            +
            end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            require "iev"
         | 
| 19 | 
            +
            require "iev/cli"
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            IEV::CLI.start(ARGV)
         | 
    
        data/iev.gemspec
    CHANGED
    
    | @@ -4,7 +4,7 @@ require "iev/version" | |
| 4 4 |  | 
| 5 5 | 
             
            Gem::Specification.new do |spec|
         | 
| 6 6 | 
             
              spec.name          = "iev"
         | 
| 7 | 
            -
              spec.version       =  | 
| 7 | 
            +
              spec.version       = IEV::VERSION
         | 
| 8 8 | 
             
              spec.authors       = ["Ribose Inc."]
         | 
| 9 9 | 
             
              spec.email         = ["open.source@ribose.com"]
         | 
| 10 10 |  | 
| @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| | |
| 19 19 | 
             
              spec.bindir        = "exe"
         | 
| 20 20 | 
             
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         | 
| 21 21 | 
             
              spec.require_paths = ["lib"]
         | 
| 22 | 
            -
              spec.required_ruby_version = Gem::Requirement.new(">=  | 
| 22 | 
            +
              spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
         | 
| 23 23 |  | 
| 24 24 | 
             
              spec.add_development_dependency "bundler"
         | 
| 25 25 | 
             
              spec.add_development_dependency "debug"
         | 
| @@ -27,5 +27,14 @@ Gem::Specification.new do |spec| | |
| 27 27 | 
             
              spec.add_development_dependency "rspec", "~> 3.0"
         | 
| 28 28 | 
             
              spec.add_development_dependency "simplecov"
         | 
| 29 29 |  | 
| 30 | 
            -
              spec.add_dependency " | 
| 30 | 
            +
              spec.add_dependency "creek", "~> 2.5"
         | 
| 31 | 
            +
              spec.add_dependency "glossarist", "~> 2.0.6"
         | 
| 32 | 
            +
              spec.add_dependency "unitsml"
         | 
| 33 | 
            +
              spec.add_dependency "plurimath"
         | 
| 34 | 
            +
              spec.add_dependency "nokogiri", ">= 1.16.5"
         | 
| 35 | 
            +
              spec.add_dependency "relaton", "~> 1.18"
         | 
| 36 | 
            +
              spec.add_dependency "sequel", "~> 5.40"
         | 
| 37 | 
            +
              spec.add_dependency "sqlite3", "~> 1.7.0"
         | 
| 38 | 
            +
              spec.add_dependency "thor", "~> 1.0"
         | 
| 39 | 
            +
              spec.add_dependency "zeitwerk", "~> 2.4"
         | 
| 31 40 | 
             
            end
         | 
| @@ -0,0 +1,109 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # (c) Copyright 2020 Ribose Inc.
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module IEV
         | 
| 7 | 
            +
              module CLI
         | 
| 8 | 
            +
                class Command < Thor
         | 
| 9 | 
            +
                  include CommandHelper
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  desc "xlsx2yaml FILE", "Converts Excel IEV exports to YAMLs."
         | 
| 12 | 
            +
                  def xlsx2yaml(file)
         | 
| 13 | 
            +
                    handle_generic_options(options)
         | 
| 14 | 
            +
                    db = Sequel.sqlite
         | 
| 15 | 
            +
                    DbWriter.new(db).import_spreadsheet(file)
         | 
| 16 | 
            +
                    ds = filter_dataset(db, options)
         | 
| 17 | 
            +
                    collection = build_collection_from_dataset(ds)
         | 
| 18 | 
            +
                    save_collection_to_files(collection, options[:output])
         | 
| 19 | 
            +
                    summary
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  desc "xlsx2db FILE", "Imports Excel to SQLite database."
         | 
| 23 | 
            +
                  def xlsx2db(file)
         | 
| 24 | 
            +
                    handle_generic_options(options)
         | 
| 25 | 
            +
                    # Instantiating an in-memory db and dumping it later is faster than
         | 
| 26 | 
            +
                    # just working on file db.
         | 
| 27 | 
            +
                    db = Sequel.sqlite
         | 
| 28 | 
            +
                    DbWriter.new(db).import_spreadsheet(file)
         | 
| 29 | 
            +
                    save_db_to_file(db, options[:output])
         | 
| 30 | 
            +
                    summary
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  desc "db2yaml DB_FILE", "Exports SQLite to IEV YAMLs."
         | 
| 34 | 
            +
                  def db2yaml(dbfile)
         | 
| 35 | 
            +
                    handle_generic_options(options)
         | 
| 36 | 
            +
                    db = Sequel.sqlite(dbfile)
         | 
| 37 | 
            +
                    ds = filter_dataset(db, options)
         | 
| 38 | 
            +
                    collection = build_collection_from_dataset(ds)
         | 
| 39 | 
            +
                    save_collection_to_files(collection, options[:output])
         | 
| 40 | 
            +
                    summary
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def self.exit_on_failure?
         | 
| 44 | 
            +
                    true
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  # Options must be declared at the bottom because Thor must have commands
         | 
| 48 | 
            +
                  # defined in advance.
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  def self.shared_option(name, methods:, **kwargs)
         | 
| 51 | 
            +
                    [*methods].each { |m| option name, for: m, **kwargs }
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  shared_option :only_concepts,
         | 
| 55 | 
            +
                    desc: "Only process concepts with IEVREF matching this argument, " +
         | 
| 56 | 
            +
                      "'%' and '_' wildcards are supported and have meaning as in SQL " +
         | 
| 57 | 
            +
                      "LIKE operator",
         | 
| 58 | 
            +
                    methods: %i[xlsx2yaml db2yaml]
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  shared_option :only_languages,
         | 
| 61 | 
            +
                    desc: "Only export these languages, skip concepts which aren't " +
         | 
| 62 | 
            +
                      "translated to any of them (comma-separated list, language " +
         | 
| 63 | 
            +
                      "codes must be as in spreadsheet)",
         | 
| 64 | 
            +
                    methods: %i[xlsx2yaml db2yaml]
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  shared_option :output,
         | 
| 67 | 
            +
                    desc: "Output directory",
         | 
| 68 | 
            +
                    aliases: :o,
         | 
| 69 | 
            +
                    default: Dir.pwd,
         | 
| 70 | 
            +
                    methods: %i[xlsx2yaml db2yaml]
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  shared_option :output,
         | 
| 73 | 
            +
                    desc: "Output file",
         | 
| 74 | 
            +
                    aliases: :o,
         | 
| 75 | 
            +
                    default: File.join(Dir.pwd, "concepts.sqlite3"),
         | 
| 76 | 
            +
                    methods: :xlsx2db
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  shared_option :progress,
         | 
| 79 | 
            +
                    type: :boolean,
         | 
| 80 | 
            +
                    desc: "Enables or disables progress indicator. By default disabled " +
         | 
| 81 | 
            +
                      "when 'CI' environment variable is set and enabled otherwise",
         | 
| 82 | 
            +
                    methods: %i[xlsx2yaml xlsx2db db2yaml]
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  shared_option :debug_term_attributes,
         | 
| 85 | 
            +
                    desc: "Enables debug messages about term attributes recognition",
         | 
| 86 | 
            +
                    type: :boolean,
         | 
| 87 | 
            +
                    default: false,
         | 
| 88 | 
            +
                    methods: %i[xlsx2yaml db2yaml]
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  shared_option :debug_sources,
         | 
| 91 | 
            +
                    desc: "Enables debug messages about authoritative sources recognition",
         | 
| 92 | 
            +
                    type: :boolean,
         | 
| 93 | 
            +
                    default: false,
         | 
| 94 | 
            +
                    methods: %i[xlsx2yaml db2yaml]
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  shared_option :debug_relaton,
         | 
| 97 | 
            +
                    desc: "Enables debug messages about Relaton integration",
         | 
| 98 | 
            +
                    type: :boolean,
         | 
| 99 | 
            +
                    default: false,
         | 
| 100 | 
            +
                    methods: %i[xlsx2yaml db2yaml]
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  shared_option :profile,
         | 
| 103 | 
            +
                    desc: "Generates profiler reports for this program, requires ruby-prof",
         | 
| 104 | 
            +
                    type: :boolean,
         | 
| 105 | 
            +
                    default: false,
         | 
| 106 | 
            +
                    methods: %i[xlsx2yaml xlsx2db db2yaml]
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
            end
         | 
| @@ -0,0 +1,83 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # (c) Copyright 2020 Ribose Inc.
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module IEV
         | 
| 7 | 
            +
              module CLI
         | 
| 8 | 
            +
                module CommandHelper
         | 
| 9 | 
            +
                  include CLI::UI
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  protected
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def save_collection_to_files(collection, output_dir)
         | 
| 14 | 
            +
                    Profiler.measure("writing-yamls") do
         | 
| 15 | 
            +
                      info "Writing concepts to files..."
         | 
| 16 | 
            +
                      path = File.expand_path("./concepts", output_dir)
         | 
| 17 | 
            +
                      FileUtils.mkdir_p(path)
         | 
| 18 | 
            +
                      collection.save_to_files(path)
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  # Note: Implementation examples here:
         | 
| 23 | 
            +
                  # https://www.rubydoc.info/github/luislavena/sqlite3-ruby/SQLite3/Backup
         | 
| 24 | 
            +
                  def save_db_to_file(src_db, dbfile)
         | 
| 25 | 
            +
                    info "Saving database to a file..."
         | 
| 26 | 
            +
                    src_db.synchronize do |src_conn|
         | 
| 27 | 
            +
                      dest_conn = SQLite3::Database.new(dbfile)
         | 
| 28 | 
            +
                      b = SQLite3::Backup.new(dest_conn, "main", src_conn, "main")
         | 
| 29 | 
            +
                      b.step(-1)
         | 
| 30 | 
            +
                      b.finish
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def summary
         | 
| 35 | 
            +
                    info "Done!"
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def collection_file_path(file, output_dir)
         | 
| 39 | 
            +
                    output_dir.join(Pathname.new(file).basename.sub_ext(".yaml"))
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  # Handles various generic options, e.g. detailed debug switches.
         | 
| 43 | 
            +
                  # Assigns some global variables accordingly, so these settings are
         | 
| 44 | 
            +
                  # available throughout the program.
         | 
| 45 | 
            +
                  def handle_generic_options(options)
         | 
| 46 | 
            +
                    $IEV_PROFILE = options[:profile]
         | 
| 47 | 
            +
                    $IEV_PROGRESS = options.fetch(:progress, !ENV["CI"])
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    $IEV_DEBUG = options.to_h.
         | 
| 50 | 
            +
                      select { |k, _| k.to_s.start_with? "debug_" }.
         | 
| 51 | 
            +
                      transform_keys { |k| k.to_s.sub("debug_", "").to_sym }
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def filter_dataset(db, options)
         | 
| 55 | 
            +
                    query = db[:concepts]
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    if options[:only_concepts]
         | 
| 58 | 
            +
                      query = query.where(Sequel.ilike(:ievref, options[:only_concepts]))
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    if options[:only_languages]
         | 
| 62 | 
            +
                      query = query.where(language: options[:only_languages].split(","))
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    query
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  def build_collection_from_dataset(dataset)
         | 
| 69 | 
            +
                    Profiler.measure("building-collection") do
         | 
| 70 | 
            +
                      Glossarist::ManagedConceptCollection.new.tap do |concept_collection|
         | 
| 71 | 
            +
                        dataset.each do |row|
         | 
| 72 | 
            +
                          term = TermBuilder.build_from(row)
         | 
| 73 | 
            +
                          if term
         | 
| 74 | 
            +
                            concept = concept_collection.fetch_or_initialize(term.id)
         | 
| 75 | 
            +
                            concept.add_l10n(term)
         | 
| 76 | 
            +
                          end
         | 
| 77 | 
            +
                        end
         | 
| 78 | 
            +
                      end
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
            end
         | 
    
        data/lib/iev/cli/ui.rb
    ADDED
    
    | @@ -0,0 +1,70 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # (c) Copyright 2020 Ribose Inc.
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module IEV
         | 
| 7 | 
            +
              module CLI
         | 
| 8 | 
            +
                # @todo
         | 
| 9 | 
            +
                #   Make it thread-safe.  Currently, calling UI methods from different
         | 
| 10 | 
            +
                #   threads may result with mangled output.  At first glance it seems like
         | 
| 11 | 
            +
                #   something is wrong with carriage returns, but more research is needed.
         | 
| 12 | 
            +
                module UI
         | 
| 13 | 
            +
                  module_function
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def debug(*args)
         | 
| 16 | 
            +
                    Helper.cli_out(:debug, *args)
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def warn(*args)
         | 
| 20 | 
            +
                    Helper.cli_out(:warn, *args)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # Prints progress message which will be replaced on next call.
         | 
| 24 | 
            +
                  def progress(message)
         | 
| 25 | 
            +
                    return unless $IEV_PROGRESS
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    print "#{Helper.clear_progress}#{message} "
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # Prints generic message.
         | 
| 31 | 
            +
                  def info(message)
         | 
| 32 | 
            +
                    print "#{Helper.clear_progress}#{message}\n"
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  # Sets an UI tag which will be prepended to messages printed with
         | 
| 36 | 
            +
                  # #debug and #warn.
         | 
| 37 | 
            +
                  def set_ui_tag(str)
         | 
| 38 | 
            +
                    Thread.current[:iev_ui_tag] = str
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  module Helper
         | 
| 42 | 
            +
                    module_function
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def clear_progress
         | 
| 45 | 
            +
                      $IEV_PROGRESS ? "\r#{" " * 40}\r" : ""
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    def cli_out(level, *args)
         | 
| 49 | 
            +
                      topic = Symbol === args[0] ? args.shift : nil
         | 
| 50 | 
            +
                      message = args.map(&:to_s).join(" ").chomp
         | 
| 51 | 
            +
                      ui_tag = Thread.current[:iev_ui_tag]
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                      return unless should_out?(level, topic)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      print [
         | 
| 56 | 
            +
                        clear_progress,
         | 
| 57 | 
            +
                        ui_tag,
         | 
| 58 | 
            +
                        ui_tag && ": ",
         | 
| 59 | 
            +
                        message,
         | 
| 60 | 
            +
                        "\n",
         | 
| 61 | 
            +
                      ].join
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    def should_out?(level, topic)
         | 
| 65 | 
            +
                      topic.nil? || level == :warn || $IEV_DEBUG[topic]
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
            end
         | 
    
        data/lib/iev/cli.rb
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # (c) Copyright 2020 Ribose Inc.
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module IEV
         | 
| 7 | 
            +
              module CLI
         | 
| 8 | 
            +
                def self.start(arguments)
         | 
| 9 | 
            +
                  Signal.trap("INT") do
         | 
| 10 | 
            +
                    UI.info "Signal SIGINT received, quitting!"
         | 
| 11 | 
            +
                    Kernel.exit(1)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  Signal.trap("TERM") do
         | 
| 15 | 
            +
                    UI.info "Signal SIGTERM received, quitting!"
         | 
| 16 | 
            +
                    Kernel.exit(1)
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  IEV::CLI::Command.start(arguments)
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,197 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module IEV
         | 
| 4 | 
            +
              module Converter
         | 
| 5 | 
            +
                class MathmlToAsciimath
         | 
| 6 | 
            +
                  using DataConversions
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def self.convert(input)
         | 
| 9 | 
            +
                    new.convert(input)
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def convert(input)
         | 
| 13 | 
            +
                    mathml_to_asciimath(input)
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  private
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def mathml_to_asciimath(input)
         | 
| 19 | 
            +
                    # If given string does not include '<' (for elements) nor '&'
         | 
| 20 | 
            +
                    # (for entities), then it's certain that it doesn't contain
         | 
| 21 | 
            +
                    # any MathML or HTML formula.
         | 
| 22 | 
            +
                    return input unless input&.match?(/<|&/)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    unless input.include?("<math>")
         | 
| 25 | 
            +
                      return html_to_asciimath(input)
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    # puts "GOING TO MATHML MATH"
         | 
| 29 | 
            +
                    # puts input
         | 
| 30 | 
            +
                    to_asciimath = Nokogiri::HTML.fragment(input, "UTF-8")
         | 
| 31 | 
            +
                    # to_asciimath.remove_namespaces!
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    to_asciimath.css("math").each do |math_element|
         | 
| 34 | 
            +
                      asciimath = Plurimath::Math.parse(
         | 
| 35 | 
            +
                        text_to_asciimath(math_element.to_xml), :mathml
         | 
| 36 | 
            +
                      ).to_asciimath.strip
         | 
| 37 | 
            +
                      # puts"ASCIIMATH!!  #{asciimath}"
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                      if asciimath.empty?
         | 
| 40 | 
            +
                        math_element.remove
         | 
| 41 | 
            +
                      else
         | 
| 42 | 
            +
                        math_element.replace "stem:[#{asciimath}]"
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    html_to_asciimath(
         | 
| 47 | 
            +
                      to_asciimath.children.to_s,
         | 
| 48 | 
            +
                    )
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def html_to_asciimath(input)
         | 
| 52 | 
            +
                    return input if input.nil? || input.empty?
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    to_asciimath = Nokogiri::HTML.fragment(input, "UTF-8")
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    to_asciimath.css("i").each do |math_element|
         | 
| 57 | 
            +
                      # puts "HTML MATH!!  #{math_element.to_xml}"
         | 
| 58 | 
            +
                      # puts "HTML MATH!!  #{math_element.text}"
         | 
| 59 | 
            +
                      decoded = text_to_asciimath(math_element.text)
         | 
| 60 | 
            +
                      case decoded.length
         | 
| 61 | 
            +
                      when 1..12
         | 
| 62 | 
            +
                        # puts "(#{math_element.text} to => #{decoded})"
         | 
| 63 | 
            +
                        math_element.replace "stem:[#{decoded}]"
         | 
| 64 | 
            +
                      when 0
         | 
| 65 | 
            +
                        math_element.remove
         | 
| 66 | 
            +
                      else
         | 
| 67 | 
            +
                        math_element.replace "_#{decoded}_"
         | 
| 68 | 
            +
                      end
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    to_asciimath.css("sub").each do |math_element|
         | 
| 72 | 
            +
                      case math_element.text.length
         | 
| 73 | 
            +
                      when 0
         | 
| 74 | 
            +
                        math_element.remove
         | 
| 75 | 
            +
                      else
         | 
| 76 | 
            +
                        math_element.replace "~#{text_to_asciimath(math_element.text)}~"
         | 
| 77 | 
            +
                      end
         | 
| 78 | 
            +
                    end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    to_asciimath.css("sup").each do |math_element|
         | 
| 81 | 
            +
                      case math_element.text.length
         | 
| 82 | 
            +
                      when 0
         | 
| 83 | 
            +
                        math_element.remove
         | 
| 84 | 
            +
                      else
         | 
| 85 | 
            +
                        math_element.replace "^#{text_to_asciimath(math_element.text)}^"
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    to_asciimath.css("ol").each do |element|
         | 
| 90 | 
            +
                      element.css("li").each do |li|
         | 
| 91 | 
            +
                        li.replace ". #{li.text}"
         | 
| 92 | 
            +
                      end
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                    to_asciimath.css("ul").each do |element|
         | 
| 96 | 
            +
                      element.css("li").each do |li|
         | 
| 97 | 
            +
                        li.replace "* #{li.text}"
         | 
| 98 | 
            +
                      end
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    # Replace sans-serif font with monospace
         | 
| 102 | 
            +
                    to_asciimath.css('font[style*="sans-serif"]').each do |x|
         | 
| 103 | 
            +
                      x.replace "`#{x.text}`"
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    html_entities_to_stem(
         | 
| 107 | 
            +
                      to_asciimath
         | 
| 108 | 
            +
                        .children.to_s
         | 
| 109 | 
            +
                        .gsub(/\]stem:\[/, "")
         | 
| 110 | 
            +
                        .gsub(/<\/?[uo]l>/, ""),
         | 
| 111 | 
            +
                    )
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  def text_to_asciimath(text)
         | 
| 115 | 
            +
                    html_entities_to_asciimath(text.decode_html)
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  def html_entities_to_asciimath(input)
         | 
| 119 | 
            +
                    input.gsub("α", "alpha").
         | 
| 120 | 
            +
                      gsub("β", "beta").
         | 
| 121 | 
            +
                      gsub("γ", "gamma").
         | 
| 122 | 
            +
                      gsub("Γ", "Gamma").
         | 
| 123 | 
            +
                      gsub("δ", "delta").
         | 
| 124 | 
            +
                      gsub("Δ", "Delta").
         | 
| 125 | 
            +
                      gsub("ε", "epsilon").
         | 
| 126 | 
            +
                      gsub("ϵ", "varepsilon").
         | 
| 127 | 
            +
                      gsub("ζ", "zeta").
         | 
| 128 | 
            +
                      gsub("η", "eta").
         | 
| 129 | 
            +
                      gsub("θ", "theta").
         | 
| 130 | 
            +
                      gsub("Θ", "Theta").
         | 
| 131 | 
            +
                      gsub("ϑ", "vartheta").
         | 
| 132 | 
            +
                      gsub("ι", "iota").
         | 
| 133 | 
            +
                      gsub("κ", "kappa").
         | 
| 134 | 
            +
                      gsub("λ", "lambda").
         | 
| 135 | 
            +
                      gsub("Λ", "Lambda").
         | 
| 136 | 
            +
                      gsub("μ", "mu").
         | 
| 137 | 
            +
                      gsub("ν", "nu").
         | 
| 138 | 
            +
                      gsub("ξ", "xi").
         | 
| 139 | 
            +
                      gsub("Ξ", "Xi").
         | 
| 140 | 
            +
                      gsub("π", "pi").
         | 
| 141 | 
            +
                      gsub("Π", "Pi").
         | 
| 142 | 
            +
                      gsub("ρ", "rho").
         | 
| 143 | 
            +
                      gsub("β", "beta").
         | 
| 144 | 
            +
                      gsub("σ", "sigma").
         | 
| 145 | 
            +
                      gsub("Σ", "Sigma").
         | 
| 146 | 
            +
                      gsub("τ", "tau").
         | 
| 147 | 
            +
                      gsub("υ", "upsilon").
         | 
| 148 | 
            +
                      gsub("φ", "phi").
         | 
| 149 | 
            +
                      gsub("Φ", "Phi").
         | 
| 150 | 
            +
                      gsub("ϕ", "varphi").
         | 
| 151 | 
            +
                      gsub("χ", "chi").
         | 
| 152 | 
            +
                      gsub("ψ", "psi").
         | 
| 153 | 
            +
                      gsub("Ψ", "Psi").
         | 
| 154 | 
            +
                      gsub("ω", "omega")
         | 
| 155 | 
            +
                  end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  def html_entities_to_stem(input)
         | 
| 158 | 
            +
                    input.gsub("α", "stem:[alpha]").
         | 
| 159 | 
            +
                      gsub("β", "stem:[beta]").
         | 
| 160 | 
            +
                      gsub("γ", "stem:[gamma]").
         | 
| 161 | 
            +
                      gsub("Γ", "stem:[Gamma]").
         | 
| 162 | 
            +
                      gsub("δ", "stem:[delta]").
         | 
| 163 | 
            +
                      gsub("Δ", "stem:[Delta]").
         | 
| 164 | 
            +
                      gsub("ε", "stem:[epsilon]").
         | 
| 165 | 
            +
                      gsub("ϵ", "stem:[varepsilon]").
         | 
| 166 | 
            +
                      gsub("ζ", "stem:[zeta]").
         | 
| 167 | 
            +
                      gsub("η", "stem:[eta]").
         | 
| 168 | 
            +
                      gsub("θ", "stem:[theta]").
         | 
| 169 | 
            +
                      gsub("Θ", "stem:[Theta]").
         | 
| 170 | 
            +
                      gsub("ϑ", "stem:[vartheta]").
         | 
| 171 | 
            +
                      gsub("ι", "stem:[iota]").
         | 
| 172 | 
            +
                      gsub("κ", "stem:[kappa]").
         | 
| 173 | 
            +
                      gsub("λ", "stem:[lambda]").
         | 
| 174 | 
            +
                      gsub("Λ", "stem:[Lambda]").
         | 
| 175 | 
            +
                      gsub("μ", "stem:[mu]").
         | 
| 176 | 
            +
                      gsub("ν", "stem:[nu]").
         | 
| 177 | 
            +
                      gsub("ξ", "stem:[xi]").
         | 
| 178 | 
            +
                      gsub("Ξ", "stem:[Xi]").
         | 
| 179 | 
            +
                      gsub("π", "stem:[pi]").
         | 
| 180 | 
            +
                      gsub("Π", "stem:[Pi]").
         | 
| 181 | 
            +
                      gsub("ρ", "stem:[rho]").
         | 
| 182 | 
            +
                      gsub("β", "stem:[beta]").
         | 
| 183 | 
            +
                      gsub("σ", "stem:[sigma]").
         | 
| 184 | 
            +
                      gsub("Σ", "stem:[Sigma]").
         | 
| 185 | 
            +
                      gsub("τ", "stem:[tau]").
         | 
| 186 | 
            +
                      gsub("υ", "stem:[upsilon]").
         | 
| 187 | 
            +
                      gsub("φ", "stem:[phi]").
         | 
| 188 | 
            +
                      gsub("Φ", "stem:[Phi]").
         | 
| 189 | 
            +
                      gsub("ϕ", "stem:[varphi]").
         | 
| 190 | 
            +
                      gsub("χ", "stem:[chi]").
         | 
| 191 | 
            +
                      gsub("ψ", "stem:[psi]").
         | 
| 192 | 
            +
                      gsub("Ψ", "stem:[Psi]").
         | 
| 193 | 
            +
                      gsub("ω", "stem:[omega]")
         | 
| 194 | 
            +
                  end
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
              end
         | 
| 197 | 
            +
            end
         |