glossarist 0.2.0 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +15 -0
  3. data/.github/workflows/release.yml +24 -0
  4. data/.gitignore +2 -0
  5. data/.hound.yml +3 -1
  6. data/.rubocop.yml +4 -29
  7. data/README.adoc +23 -0
  8. data/config.yml +83 -0
  9. data/exe/glossarist +50 -0
  10. data/glossarist.gemspec +4 -1
  11. data/lib/glossarist/asset.rb +22 -0
  12. data/lib/glossarist/citation.rb +89 -0
  13. data/lib/glossarist/collections/asset_collection.rb +11 -0
  14. data/lib/glossarist/collections/bibliography_collection.rb +25 -0
  15. data/lib/glossarist/collections.rb +2 -0
  16. data/lib/glossarist/concept.rb +96 -31
  17. data/lib/glossarist/concept_date.rb +20 -0
  18. data/lib/glossarist/concept_manager.rb +44 -0
  19. data/lib/glossarist/concept_set.rb +80 -0
  20. data/lib/glossarist/concept_source.rb +62 -0
  21. data/lib/glossarist/config.rb +54 -0
  22. data/lib/glossarist/designation/abbreviation.rb +23 -0
  23. data/lib/glossarist/designation/base.rb +37 -0
  24. data/lib/glossarist/designation/expression.rb +50 -0
  25. data/lib/glossarist/designation/grammar_info.rb +58 -0
  26. data/lib/glossarist/designation/graphical_symbol.rb +17 -0
  27. data/lib/glossarist/designation/letter_symbol.rb +19 -0
  28. data/lib/glossarist/designation/symbol.rb +21 -0
  29. data/lib/glossarist/designation.rb +27 -0
  30. data/lib/glossarist/detailed_definition.rb +31 -0
  31. data/lib/glossarist/glossary_definition.rb +29 -0
  32. data/lib/glossarist/localized_concept.rb +10 -59
  33. data/lib/glossarist/managed_concept.rb +116 -0
  34. data/lib/glossarist/managed_concept_collection.rb +79 -0
  35. data/lib/glossarist/model.rb +12 -2
  36. data/lib/glossarist/non_verb_rep.rb +18 -0
  37. data/lib/glossarist/related_concept.rb +31 -0
  38. data/lib/glossarist/utilities/boolean_attributes.rb +35 -0
  39. data/lib/glossarist/utilities/common_functions.rb +29 -0
  40. data/lib/glossarist/utilities/enum/class_methods.rb +99 -0
  41. data/lib/glossarist/utilities/enum/enum_collection.rb +45 -0
  42. data/lib/glossarist/utilities/enum/instance_methods.rb +55 -0
  43. data/lib/glossarist/utilities/enum.rb +21 -0
  44. data/lib/glossarist/utilities.rb +5 -0
  45. data/lib/glossarist/version.rb +1 -1
  46. data/lib/glossarist.rb +27 -2
  47. metadata +69 -7
  48. data/.github/workflows/test.yml +0 -34
  49. data/lib/glossarist/designations.rb +0 -82
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ class ConceptSet
5
+ # a `Glossarist::ManagedConceptCollection` object
6
+ attr_accessor :concepts
7
+
8
+ # a `BibliographyCollection` object
9
+ attr_accessor :bibliographies
10
+
11
+ # an `Collections::Asset` object
12
+ attr_accessor :assets
13
+
14
+ # @parameters
15
+ # concepts => a `Glossarist::ManagedConceptCollection` object or
16
+ # a string containing the path of the folder with concepts
17
+ # assets => a collection of Glossarist::Asset
18
+ def initialize(concepts, assets, options = {})
19
+ @concepts = read_concepts(concepts)
20
+ @assets = Glossarist::Collections::AssetCollection.new(assets)
21
+ @bibliographies = Glossarist::Collections::BibliographyCollection.new(
22
+ @concepts,
23
+ options.dig(:bibliography, :global_cache),
24
+ options.dig(:bibliography, :local_cache),
25
+ )
26
+ end
27
+
28
+ def to_latex(filename = nil)
29
+ return to_latex_from_file(filename) if filename
30
+
31
+ @concepts.map do |concept|
32
+ latex_template(concept)
33
+ end.join("\n")
34
+ end
35
+
36
+ private
37
+
38
+ def to_latex_from_file(entries_file)
39
+ File.readlines(entries_file).map do |concept_name|
40
+ concept = concept_map[concept_name.strip.downcase]
41
+
42
+ if concept.nil?
43
+ puts " [Not Found]: #{concept_name.strip}"
44
+ else
45
+ latex_template(concept)
46
+ end
47
+ end.compact.join("\n")
48
+ end
49
+
50
+ def read_concepts(concepts)
51
+ return concepts if concepts.is_a?(Glossarist::ManagedConceptCollection)
52
+
53
+ collection = Glossarist::ManagedConceptCollection.new
54
+ collection.load_from_files(concepts)
55
+ collection
56
+ end
57
+
58
+ def latex_template(concept)
59
+ <<~TEMPLATE
60
+ \\newglossaryentry{#{concept.default_designation.gsub('_', '-')}}
61
+ {
62
+ name={#{concept.default_designation.gsub('_', '\_')}}
63
+ description={#{normalize_definition(concept.default_definition)}}
64
+ }
65
+ TEMPLATE
66
+ end
67
+
68
+ def normalize_definition(definition)
69
+ definition.gsub(/{{([^}]*)}}/) do |match|
70
+ "\\textbf{\\gls{#{Regexp.last_match[1].gsub('_', '-')}}}"
71
+ end
72
+ end
73
+
74
+ def concept_map
75
+ @concept_map ||= concepts.managed_concepts.map do |concept|
76
+ [concept.default_designation.downcase, concept]
77
+ end.to_h
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ class ConceptSource < Model
5
+ include Glossarist::Utilities::Enum
6
+ include Glossarist::Utilities::CommonFunctions
7
+
8
+ register_enum :status, Glossarist::GlossaryDefinition::CONCEPT_SOURCE_STATUSES
9
+ register_enum :type, Glossarist::GlossaryDefinition::CONCEPT_SOURCE_TYPES
10
+
11
+ attr_reader :origin
12
+ alias_method :ref, :origin
13
+
14
+ attr_accessor :modification
15
+
16
+ def initialize(attributes = {})
17
+ if rel = attributes.delete("relationship")
18
+ self.status = rel["type"]
19
+ self.modification = rel["modification"]
20
+ end
21
+
22
+ self.origin = slice_keys(attributes, ref_param_names)
23
+
24
+ remaining_attributes = attributes.dup
25
+ ref_param_names.each { |k| remaining_attributes.delete(k) }
26
+
27
+ super(remaining_attributes)
28
+ end
29
+
30
+ def origin=(origin)
31
+ @origin = Citation.new(origin)
32
+ end
33
+
34
+ alias_method :ref=, :origin=
35
+
36
+ def to_h
37
+ origin_hash = self.origin.to_h.empty? ? nil : self.origin.to_h
38
+
39
+ {
40
+ "type" => type.to_s,
41
+ "status" => status&.to_s,
42
+ "origin" => origin_hash,
43
+ "modification" => modification,
44
+ }.compact
45
+ end
46
+
47
+ private
48
+
49
+ def ref_param_names
50
+ %w[
51
+ ref
52
+ text
53
+ source
54
+ id
55
+ version
56
+ clause
57
+ link
58
+ original
59
+ ]
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+
5
+ module Glossarist
6
+ class Config
7
+ include Singleton
8
+
9
+ DEFAULT_CLASSES = {
10
+ localized_concept: Glossarist::LocalizedConcept,
11
+ }.freeze
12
+
13
+ attr_reader :registered_classes
14
+
15
+ def initialize
16
+ @registered_classes = DEFAULT_CLASSES.dup
17
+ @extension_attributes = []
18
+ end
19
+
20
+ def class_for(name)
21
+ @registered_classes[name.to_sym]
22
+ end
23
+
24
+ def register_class(class_name, klass)
25
+ @registered_classes[class_name] = klass
26
+ end
27
+
28
+ def extension_attributes
29
+ @extension_attributes
30
+ end
31
+
32
+ def register_extension_attributes(attributes)
33
+ @extension_attributes = attributes
34
+ end
35
+
36
+ class << self
37
+ def class_for(name)
38
+ self.instance.class_for(name)
39
+ end
40
+
41
+ def extension_attributes
42
+ self.instance.extension_attributes
43
+ end
44
+
45
+ def register_class(class_name, klass)
46
+ self.instance.register_class(class_name, klass)
47
+ end
48
+
49
+ def register_extension_attributes(attributes)
50
+ self.register_extension_attributes(attributes)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "expression"
4
+ require_relative "../utilities"
5
+
6
+ module Glossarist
7
+ module Designation
8
+ class Abbreviation < Expression
9
+ include Glossarist::Utilities::Enum
10
+
11
+ register_enum :type, Glossarist::GlossaryDefinition::ABBREVIATION_TYPES
12
+
13
+ attr_accessor :international
14
+
15
+ def to_h
16
+ super().merge({
17
+ "type" => type.to_s,
18
+ "international" => international,
19
+ }).compact
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Designation
5
+ class Base < Model
6
+ include Glossarist::Utilities::Enum
7
+
8
+ # @note This is not entirely aligned with agreed schema and may be
9
+ # changed.
10
+ attr_accessor :designation
11
+
12
+ attr_accessor :geographical_area
13
+ register_enum :normative_status, Glossarist::GlossaryDefinition::DESIGNATION_BASE_NORMATIVE_STATUSES
14
+
15
+ def self.from_h(hash)
16
+ type = hash["type"]
17
+
18
+ if type.nil? || /\w/ !~ type
19
+ raise ArgumentError, "designation type is missing"
20
+ end
21
+
22
+ designation_subclass = SERIALIZED_TYPES[type]
23
+
24
+ if self == Base
25
+ # called on Base class, delegate it to proper subclass
26
+ SERIALIZED_TYPES[type].from_h(hash)
27
+ else
28
+ # called on subclass, instantiate object
29
+ unless SERIALIZED_TYPES[self] == type
30
+ raise ArgumentError, "unexpected designation type: #{type}"
31
+ end
32
+ super(hash.reject { |k, _| k == "type" })
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Glossarist
6
+ module Designation
7
+ class Expression < Base
8
+ attr_accessor :prefix
9
+ attr_accessor :usage_info
10
+
11
+ # List of grammar info.
12
+ # @return [Array<GrammarInfo>]
13
+ attr_reader :grammar_info
14
+
15
+ def grammar_info=(grammar_info)
16
+ @grammar_info = grammar_info.map { |g| GrammarInfo.new(g) }
17
+ end
18
+
19
+ # @todo Added to cater for current iev-data implementation,
20
+ # might be removed in the future.
21
+ def self.from_h(hash)
22
+ gender = hash.delete("gender") || hash.delete(:gender)
23
+ number = hash.delete("plurality") || hash.delete(:plurality)
24
+ part_of_speech = hash.delete("part_of_speech") || hash.delete(:part_of_speech)
25
+
26
+ if gender || number || part_of_speech
27
+ hash["grammar_info"] = [{
28
+ "gender" => gender,
29
+ "number" => number,
30
+ part_of_speech => part_of_speech,
31
+ }.compact]
32
+ end
33
+
34
+ super
35
+ end
36
+
37
+ def to_h
38
+ {
39
+ "type" => "expression",
40
+ "prefix" => prefix,
41
+ "normative_status" => normative_status,
42
+ "usage_info" => usage_info,
43
+ "designation" => designation,
44
+ "geographical_area" => geographical_area,
45
+ "grammar_info" => grammar_info&.map(&:to_h),
46
+ }.compact
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../utilities"
4
+
5
+ module Glossarist
6
+ module Designation
7
+ class GrammarInfo
8
+ include Glossarist::Utilities::Enum
9
+ include Glossarist::Utilities::BooleanAttributes
10
+ include Glossarist::Utilities::CommonFunctions
11
+
12
+ register_enum :gender, Glossarist::GlossaryDefinition::GRAMMAR_INFO_GENDERS, multiple: true
13
+ register_enum :number, Glossarist::GlossaryDefinition::GRAMMAR_INFO_NUMBERS, multiple: true
14
+
15
+ register_boolean_attributes Glossarist::GlossaryDefinition::GRAMMAR_INFO_BOOLEAN_ATTRIBUTES
16
+
17
+ def initialize(options = {})
18
+ sanitized_options(options).each do |attr, value|
19
+ public_send("#{attr}=", value)
20
+ end
21
+ end
22
+
23
+ def part_of_speech=(pos)
24
+ public_send("#{pos}=", pos)
25
+ end
26
+
27
+ def to_h
28
+ {
29
+ "preposition" => preposition?,
30
+ "participle" => participle?,
31
+ "adj" => adj?,
32
+ "verb" => verb?,
33
+ "adverb" => adverb?,
34
+ "noun" => noun?,
35
+ "gender" => gender,
36
+ "number" => number,
37
+ }
38
+ end
39
+
40
+ private
41
+
42
+ def sanitized_options(options)
43
+ hash = symbolize_keys(options)
44
+ slice_keys(hash, [
45
+ :gender,
46
+ :number,
47
+ :preposition,
48
+ :participle,
49
+ :adj,
50
+ :verb,
51
+ :adverb,
52
+ :noun,
53
+ :part_of_speech,
54
+ ])
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Designation
5
+ class GraphicalSymbol < Symbol
6
+ attr_accessor :text
7
+ attr_accessor :image
8
+
9
+ def to_h
10
+ super.merge(
11
+ "text" => text,
12
+ "image" => image,
13
+ )
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Designation
5
+ class LetterSymbol < Symbol
6
+ attr_accessor :text
7
+ attr_accessor :language
8
+ attr_accessor :script
9
+
10
+ def to_h
11
+ super.merge(
12
+ "text" => text,
13
+ "language" => language,
14
+ "script" => script,
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Glossarist
6
+ module Designation
7
+ class Symbol < Base
8
+ attr_accessor :international
9
+
10
+ def to_h
11
+ {
12
+ "type" => Glossarist::Designation::SERIALIZED_TYPES[self.class],
13
+ "normative_status" => normative_status,
14
+ "geographical_area" => geographical_area,
15
+ "designation" => designation,
16
+ "international" => international,
17
+ }.compact
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) Copyright 2021 Ribose Inc.
4
+ #
5
+
6
+ require_relative "designation/abbreviation"
7
+ require_relative "designation/base"
8
+ require_relative "designation/expression"
9
+ require_relative "designation/grammar_info"
10
+ require_relative "designation/symbol"
11
+ require_relative "designation/graphical_symbol"
12
+ require_relative "designation/letter_symbol"
13
+
14
+ module Glossarist
15
+ module Designation
16
+ # Bi-directional class-to-string mapping for STI-like serialization.
17
+ SERIALIZED_TYPES = {
18
+ Expression => "expression",
19
+ Symbol => "symbol",
20
+ Abbreviation => "abbreviation",
21
+ GraphicalSymbol => "graphical_symbol",
22
+ LetterSymbol => "letter_symbol",
23
+ }
24
+ .tap { |h| h.merge!(h.invert) }
25
+ .freeze
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ class DetailedDefinition < Model
5
+
6
+ def initialize(attributes = {})
7
+ if attributes.is_a?(Hash)
8
+ super
9
+ else
10
+ self.content = attributes
11
+ end
12
+ end
13
+
14
+ # @return [String]
15
+ attr_accessor :content
16
+
17
+ # @return [Array<ConceptSource>]
18
+ attr_reader :sources
19
+
20
+ def sources=(sources)
21
+ @sources = sources.map { |s| ConceptSource.new(s) }
22
+ end
23
+
24
+ def to_h
25
+ {
26
+ "content" => content,
27
+ "sources" => sources&.map(&:to_h),
28
+ }.compact
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Glossarist
6
+ module GlossaryDefinition
7
+ config = YAML.load_file(File.expand_path("../../../config.yml", __FILE__)) || {}
8
+
9
+ CONCEPT_SOURCE_STATUSES = config.dig("concept_source", "status").freeze
10
+
11
+ CONCEPT_SOURCE_TYPES = config.dig("concept_source", "type").freeze
12
+
13
+ RELATED_CONCEPT_TYPES = config.dig("related_concept", "type").freeze
14
+
15
+ ABBREVIATION_TYPES = config.dig("abbreviation", "type").freeze
16
+
17
+ GRAMMAR_INFO_BOOLEAN_ATTRIBUTES = config.dig("grammar_info", "boolean_attribute").freeze
18
+
19
+ GRAMMAR_INFO_GENDERS = config.dig("grammar_info", "gender").freeze
20
+
21
+ GRAMMAR_INFO_NUMBERS = config.dig("grammar_info", "number").freeze
22
+
23
+ DESIGNATION_BASE_NORMATIVE_STATUSES = config.dig("designation", "base", "normative_status").freeze
24
+
25
+ CONCEPT_DATE_TYPES = config.dig("concept_date", "type").freeze
26
+
27
+ CONCEPT_STATUSES = config.dig("concept", "status").freeze
28
+ end
29
+ end
@@ -4,43 +4,12 @@
4
4
  #
5
5
 
6
6
  module Glossarist
7
- class LocalizedConcept < Model
8
- # Needs to be identical with {Concept#id}.
9
- # @todo Here for legacy reasons. Will be removed eventually.
10
- attr_accessor :id
11
-
7
+ class LocalizedConcept < Concept
12
8
  # ISO 639-2 code for terminology.
13
9
  # @see https://www.loc.gov/standards/iso639-2/php/code_list.php code list
14
10
  # @return [String]
15
11
  attr_accessor :language_code
16
12
 
17
- # Concept designations.
18
- # @todo Alias +terms+ exists only for legacy reasons and will be removed.
19
- # @return [Array<Designations::Base>]
20
- attr_accessor :designations
21
- alias :terms :designations
22
- alias :terms= :designations=
23
-
24
- # @return [Array<String>]
25
- attr_accessor :notes
26
-
27
- # @return [Array<String>]
28
- attr_accessor :examples
29
-
30
- # Concept definition.
31
- # @todo Support multiple definitions.
32
- # @return [String]
33
- attr_accessor :definition
34
-
35
- # @todo Right now accepts hashes for legacy reasons, but they will be
36
- # replaced with dedicated classes.
37
- # @todo Alias +authoritative_source+ exists for legacy reasons and may be
38
- # removed.
39
- # @return [Array<Hash>]
40
- attr_accessor :sources
41
- alias :authoritative_source :sources
42
- alias :authoritative_source= :sources=
43
-
44
13
  # Must be one of the following:
45
14
  # +notValid+, +valid+, +superseded+, +retired+.
46
15
  # @todo Proper type checking.
@@ -59,48 +28,30 @@ module Glossarist
59
28
  attr_accessor :review_decision_date
60
29
  attr_accessor :review_decision_event
61
30
 
62
- attr_accessor :date_accepted
63
- attr_accessor :date_amended
64
-
65
- # @todo Here for legacy reasons. Will be moved to Concept.
66
- # @todo Right now is an array of hashes for legacy reasons, but these hashes
67
- # will be replaced with some dedicated class.
68
- # @todo Should be read-only, but for now it is not for legacy reasons.
69
- # Don't use the setter.
70
- # @return [Array<Hash>]
71
- attr_accessor :superseded_concepts
72
-
73
31
  def initialize(*)
74
32
  @examples = []
75
- @notes = []
76
- @designations = []
77
- @superseded_concepts = []
78
- @sources = []
33
+
79
34
  super
80
35
  end
81
36
 
82
37
  def to_h # rubocop:disable Metrics/MethodLength
83
- {
84
- "id" => id,
85
- "terms" => (terms&.map(&:to_h) || []),
86
- "definition" => definition,
38
+ super.merge({
87
39
  "language_code" => language_code,
88
- "notes" => notes,
89
- "examples" => examples,
90
40
  "entry_status" => entry_status,
41
+ "sources" => sources.empty? ? nil : sources&.map(&:to_h),
91
42
  "classification" => classification,
92
- "authoritative_source" => (sources if sources&.any?),
93
- "date_accepted" => date_accepted,
94
- "date_amended" => date_amended,
43
+ "dates" => dates&.map(&:to_h),
95
44
  "review_date" => review_date,
96
45
  "review_decision_date" => review_decision_date,
97
46
  "review_decision_event" => review_decision_event,
98
- }.compact
47
+ }.compact).merge(@extension_attributes)
99
48
  end
100
49
 
101
50
  def self.from_h(hash)
102
- terms = hash["terms"]&.map { |h| Designations::Base.from_h(h) } || []
103
- super(hash.merge({"terms" => terms}))
51
+ terms = hash["terms"]&.map { |h| Designation::Base.from_h(h) } || []
52
+ sources = hash["authoritative_source"]&.each { |source| source.merge({ "type" => "authoritative"}) }
53
+
54
+ super(hash.merge({"terms" => terms, "sources" => sources}))
104
55
  end
105
56
 
106
57
  # @deprecated For legacy reasons only.