iev 0.3.1 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cca9ca5a2a313fce390cb65045716edd91c671375f3f6b9abd65b57a7dc78c7
4
- data.tar.gz: 87726ed4c13880b63483f244683e8c5018a108ef181c8f2b1979a0825998420b
3
+ metadata.gz: a5909046a19d741d4242dd6823366923efb0607de3ebf89fdcb99465310b6150
4
+ data.tar.gz: 55812547af56e58d9ea68a1cc7413632558c51a8d3b0d22b48ee61e81afef390
5
5
  SHA512:
6
- metadata.gz: 2f47e2ef08727216cb287dd520bba49fc89ceca6e4b9c81fff402056b80267cd888c19d462fd68dff413a4f9742ecaa6b8dec90fa17fbcab52fdbb7be3e51ed8
7
- data.tar.gz: 6c6f1f7b74a955504625a8fb88a79f302c04571151dad59981644777fec206f06eb612bf5a947f5327e42598ab23c818f26979c73f9c09035bfde9146d69ef60
6
+ metadata.gz: 6e5cca48906d1f65292cc58887b89564ae293da6cd081e47e70495d11f43c606b6db11d7d99db92210909b4fc3ccfcb54ffdb003191c17134f15c921cbded6c0
7
+ data.tar.gz: 02f500f0bf2c1da875402a5f26f709d86ee7a4924fd4dd61796e653e260cd52d6c88fc37c9403bf4db479bdeb59664bc2cd599eaa00f40012ca0893a35a96164
@@ -10,6 +10,6 @@ on:
10
10
 
11
11
  jobs:
12
12
  rake:
13
- uses: metanorma/metanorma-build-scripts/.github/workflows/generic-rake.yml@main
13
+ uses: metanorma/ci/.github/workflows/generic-rake.yml@main
14
14
  secrets:
15
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
@@ -2,6 +2,7 @@
2
2
  /.yardoc
3
3
  /_yardoc/
4
4
  /coverage/
5
+ /db/
5
6
  /doc/
6
7
  /pkg/
7
8
  /spec/reports/
@@ -11,3 +12,6 @@
11
12
 
12
13
  # rspec failure tracking
13
14
  .rspec_status
15
+ .vscode/
16
+
17
+ concepts/
data/.rubocop.yml CHANGED
@@ -6,5 +6,3 @@ inherit_from:
6
6
  # local repo-specific modifications
7
7
  # ...
8
8
 
9
- AllCops:
10
- TargetRubyVersion: 2.5
data/README.adoc CHANGED
@@ -1,4 +1,4 @@
1
- = Iev
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
- Iev.get("103-01-02", "en")
34
+ IEV.get("103-01-02", "en")
35
35
  => "functional"
36
36
 
37
37
  # If code not found
38
- Iev.get("111-11-11", "en")
38
+ IEV.get("111-11-11", "en")
39
39
  => ""
40
40
 
41
41
  # If language not found
42
- Iev.get("103-01-02", "eee")
42
+ IEV.get("103-01-02", "eee")
43
43
  => nil
44
44
  ----
45
45
 
@@ -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 = Iev::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(">= 2.5.0")
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 "nokogiri", "~> 1"
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;", "alpha").
120
+ gsub("&beta;", "beta").
121
+ gsub("&gamma;", "gamma").
122
+ gsub("&Gamma;", "Gamma").
123
+ gsub("&delta;", "delta").
124
+ gsub("&Delta;", "Delta").
125
+ gsub("&epsilon;", "epsilon").
126
+ gsub("&varepsilon;", "varepsilon").
127
+ gsub("&zeta;", "zeta").
128
+ gsub("&eta;", "eta").
129
+ gsub("&theta;", "theta").
130
+ gsub("&Theta;", "Theta").
131
+ gsub("&vartheta;", "vartheta").
132
+ gsub("&iota;", "iota").
133
+ gsub("&kappa;", "kappa").
134
+ gsub("&lambda;", "lambda").
135
+ gsub("&Lambda;", "Lambda").
136
+ gsub("&mu;", "mu").
137
+ gsub("&nu;", "nu").
138
+ gsub("&xi;", "xi").
139
+ gsub("&Xi;", "Xi").
140
+ gsub("&pi;", "pi").
141
+ gsub("&Pi;", "Pi").
142
+ gsub("&rho;", "rho").
143
+ gsub("&beta;", "beta").
144
+ gsub("&sigma;", "sigma").
145
+ gsub("&Sigma;", "Sigma").
146
+ gsub("&tau;", "tau").
147
+ gsub("&upsilon;", "upsilon").
148
+ gsub("&phi;", "phi").
149
+ gsub("&Phi;", "Phi").
150
+ gsub("&varphi;", "varphi").
151
+ gsub("&chi;", "chi").
152
+ gsub("&psi;", "psi").
153
+ gsub("&Psi;", "Psi").
154
+ gsub("&omega;", "omega")
155
+ end
156
+
157
+ def html_entities_to_stem(input)
158
+ input.gsub("&alpha;", "stem:[alpha]").
159
+ gsub("&beta;", "stem:[beta]").
160
+ gsub("&gamma;", "stem:[gamma]").
161
+ gsub("&Gamma;", "stem:[Gamma]").
162
+ gsub("&delta;", "stem:[delta]").
163
+ gsub("&Delta;", "stem:[Delta]").
164
+ gsub("&epsilon;", "stem:[epsilon]").
165
+ gsub("&varepsilon;", "stem:[varepsilon]").
166
+ gsub("&zeta;", "stem:[zeta]").
167
+ gsub("&eta;", "stem:[eta]").
168
+ gsub("&theta;", "stem:[theta]").
169
+ gsub("&Theta;", "stem:[Theta]").
170
+ gsub("&vartheta;", "stem:[vartheta]").
171
+ gsub("&iota;", "stem:[iota]").
172
+ gsub("&kappa;", "stem:[kappa]").
173
+ gsub("&lambda;", "stem:[lambda]").
174
+ gsub("&Lambda;", "stem:[Lambda]").
175
+ gsub("&mu;", "stem:[mu]").
176
+ gsub("&nu;", "stem:[nu]").
177
+ gsub("&xi;", "stem:[xi]").
178
+ gsub("&Xi;", "stem:[Xi]").
179
+ gsub("&pi;", "stem:[pi]").
180
+ gsub("&Pi;", "stem:[Pi]").
181
+ gsub("&rho;", "stem:[rho]").
182
+ gsub("&beta;", "stem:[beta]").
183
+ gsub("&sigma;", "stem:[sigma]").
184
+ gsub("&Sigma;", "stem:[Sigma]").
185
+ gsub("&tau;", "stem:[tau]").
186
+ gsub("&upsilon;", "stem:[upsilon]").
187
+ gsub("&phi;", "stem:[phi]").
188
+ gsub("&Phi;", "stem:[Phi]").
189
+ gsub("&varphi;", "stem:[varphi]").
190
+ gsub("&chi;", "stem:[chi]").
191
+ gsub("&psi;", "stem:[psi]").
192
+ gsub("&Psi;", "stem:[Psi]").
193
+ gsub("&omega;", "stem:[omega]")
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IEV
4
+ module Converter
5
+ def self.mathml_to_asciimath(input)
6
+ IEV::Converter::MathmlToAsciimath.convert(input)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) Copyright 2020 Ribose Inc.
4
+ #
5
+
6
+ module IEV
7
+ module DataConversions
8
+ refine String do
9
+ def decode_html!
10
+ replace(decode_html)
11
+ nil
12
+ end
13
+
14
+ def decode_html
15
+ HTMLEntities.new(:expanded).decode(self)
16
+ end
17
+
18
+ # Normalize various encoding anomalies like `\uFEFF` in strings
19
+ def sanitize!
20
+ unicode_normalize!
21
+ gsub!("\uFEFF", "")
22
+ gsub!("\u2011", "-")
23
+ gsub!("\u00a0", " ")
24
+ gsub!(/[\u2000-\u2006]/, " ")
25
+ strip!
26
+ nil
27
+ end
28
+
29
+ # @see sanitize!
30
+ def sanitize
31
+ dup.tap(&:sanitize!)
32
+ end
33
+
34
+ def to_three_char_code
35
+ IEV::Iso639Code.three_char_code(self).first
36
+ end
37
+ end
38
+ end
39
+ end