glossarist 0.2.0 → 1.0.5

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.
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,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ class ManagedConcept < Model
5
+ include Glossarist::Utilities::Enum
6
+ include Glossarist::Utilities::CommonFunctions
7
+
8
+ # @return [String]
9
+ attr_accessor :id
10
+ alias :termid= :id=
11
+
12
+ # @return [Array<RelatedConcept>]
13
+ attr_reader :related
14
+
15
+ # @return [String]
16
+ register_enum :status, Glossarist::GlossaryDefinition::CONCEPT_STATUSES
17
+
18
+ # return [Array<ConceptDate>]
19
+ attr_reader :dates
20
+
21
+ # return [Array<LocalizedConcept>]
22
+ attr_reader :localized_concepts
23
+
24
+ # All localizations for this concept.
25
+ #
26
+ # Keys are language codes and values are instances of {LocalizedConcept}.
27
+ # @return [Hash<String, LocalizedConcept>]
28
+ attr_accessor :localizations
29
+
30
+ def initialize(attributes = {})
31
+ @localizations = {}
32
+ self.localized_concepts = attributes.values.grep(Hash)
33
+
34
+ attributes = symbolize_keys(attributes)
35
+ super(slice_keys(attributes, managed_concept_attributes))
36
+ end
37
+
38
+ def localized_concepts=(localized_concepts_hash)
39
+ @localized_concepts = localized_concepts_hash.map { |l| Config.class_for(:localized_concept).new(l) }.compact
40
+
41
+ @localized_concepts.each do |l|
42
+ add_l10n(l)
43
+ end
44
+
45
+ @localized_concepts
46
+ end
47
+
48
+ def related=(related)
49
+ @related = related&.map { |r| RelatedConcept.new(r) }
50
+ end
51
+
52
+ def dates=(dates)
53
+ @dates = dates&.map { |d| ConceptDate.new(d) }
54
+ end
55
+
56
+ # Adds concept localization.
57
+ # @param localized_concept [LocalizedConcept]
58
+ def add_localization(localized_concept)
59
+ lang = localized_concept.language_code
60
+ localizations.store(lang, localized_concept)
61
+ end
62
+
63
+ alias :add_l10n :add_localization
64
+
65
+ # Returns concept localization.
66
+ # @param lang [String] language code
67
+ # @return [LocalizedConcept]
68
+ def localization(lang)
69
+ localizations[lang]
70
+ end
71
+
72
+ alias :l10n :localization
73
+
74
+ def to_h
75
+ {
76
+ "termid" => id,
77
+ "term" => default_designation,
78
+ "related" => related&.map(&:to_h),
79
+ "dates" => dates&.empty? ? nil : dates&.map(&:to_h),
80
+ }.merge(localizations.transform_values(&:to_h)).compact
81
+ end
82
+
83
+ def default_designation
84
+ localized = localization("eng") || localizations.values.first
85
+ localized&.terms&.first&.designation
86
+ end
87
+
88
+ def default_definition
89
+ localized = localization("eng") || localizations.values.first
90
+ localized&.definition&.first&.content
91
+ end
92
+
93
+ def default_lang
94
+ localization("eng") || localizations.values.first
95
+ end
96
+
97
+ def managed_concept_attributes
98
+ %i[
99
+ id
100
+ termid
101
+ related
102
+ status
103
+ dates
104
+ localized_concepts
105
+ ].compact
106
+ end
107
+
108
+ Glossarist::GlossaryDefinition::RELATED_CONCEPT_TYPES.each do |type|
109
+ # List of related concepts of the specified type.
110
+ # @return [Array<RelatedConcept>]
111
+ define_method("#{type}_concepts") do
112
+ related&.select { |concept| concept.type == type.to_s } || []
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ class ManagedConceptCollection
5
+ include Enumerable
6
+
7
+ def initialize
8
+ @managed_concepts = {}
9
+ @concept_manager = ConceptManager.new
10
+ end
11
+
12
+ # @return [Array<ManagedConcept>]
13
+ def managed_concepts
14
+ @managed_concepts.values
15
+ end
16
+
17
+ def managed_concepts=(managed_concepts = [])
18
+ managed_concepts.each do |managed_concept|
19
+ store(ManagedConcept.new(managed_concept))
20
+ end
21
+
22
+ @managed_concepts.values
23
+ end
24
+
25
+ def to_h
26
+ {
27
+ "managed_concepts" => managed_concepts.map(&:to_h),
28
+ }.compact
29
+ end
30
+
31
+ def each(&block)
32
+ @managed_concepts.each_value(&block)
33
+ end
34
+
35
+ # Returns concept with given ID, if it is present in collection, or +nil+
36
+ # otherwise.
37
+ #
38
+ # @param id [String]
39
+ # ManagedConcept ID
40
+ # @return [ManagedConcept, nil]
41
+ def fetch(id)
42
+ @managed_concepts[id]
43
+ end
44
+
45
+ alias :[] :fetch
46
+
47
+ # If ManagedConcept with given ID is present in this collection, then
48
+ # returns it. Otherwise, instantiates a new ManagedConcept, adds it to
49
+ # the collection, and returns it.
50
+ #
51
+ # @param id [String]
52
+ # ManagedConcept ID
53
+ # @return [ManagedConcept]
54
+ def fetch_or_initialize(id)
55
+ fetch(id) or store(ManagedConcept.new(id: id))
56
+ end
57
+
58
+ # Adds concept to the collection. If collection contains a concept with
59
+ # the same ID already, that concept is replaced.
60
+ #
61
+ # @param managed_concept [ManagedConcept]
62
+ # ManagedConcept about to be added
63
+ def store(managed_concept)
64
+ @managed_concepts[managed_concept.id] = managed_concept
65
+ end
66
+
67
+ alias :<< :store
68
+
69
+ def load_from_files(path)
70
+ @concept_manager.path = path
71
+ @concept_manager.load_from_files(collection: self)
72
+ end
73
+
74
+ def save_to_files(path)
75
+ @concept_manager.path = path
76
+ @concept_manager.save_to_files(@managed_concepts)
77
+ end
78
+ end
79
+ end
@@ -5,6 +5,12 @@
5
5
 
6
6
  module Glossarist
7
7
  class Model
8
+ def self.new(params = {})
9
+ return params if params.is_a?(self)
10
+
11
+ super
12
+ end
13
+
8
14
  def initialize(attributes = {})
9
15
  attributes.each_pair { |k, v| set_attribute(k, v) }
10
16
  end
@@ -12,8 +18,12 @@ module Glossarist
12
18
  def set_attribute(name, value)
13
19
  public_send("#{name}=", value)
14
20
  rescue NoMethodError
15
- raise ArgumentError, "#{self.class.name} does not have " +
16
- "attribute #{name} defined or the attribute is read only."
21
+ if Config.extension_attributes.include?(name)
22
+ extension_attributes[name] = value
23
+ else
24
+ raise ArgumentError, "#{self.class.name} does not have " +
25
+ "attribute #{name} defined or the attribute is read only."
26
+ end
17
27
  end
18
28
 
19
29
  def self.from_h(hash)
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ class NonVerbRep
5
+ attr_accessor :image
6
+ attr_accessor :table
7
+ attr_accessor :formula
8
+
9
+ # @return [Array<ConceptSource>]
10
+ attr_reader :sources
11
+
12
+ def sources=(sources)
13
+ @sources = sources&.map do |source|
14
+ ConceptSource.new(source)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ class RelatedConcept < Model
5
+ include Glossarist::Utilities::Enum
6
+
7
+ register_enum :type, Glossarist::GlossaryDefinition::RELATED_CONCEPT_TYPES
8
+
9
+ # @return [String]
10
+ attr_accessor :content
11
+
12
+ # Reference to the related concept.
13
+ # @return [Citation]
14
+ attr_reader :ref
15
+
16
+ def ref=(ref)
17
+ @ref = Citation.new(ref)
18
+ end
19
+
20
+ def to_h
21
+ reference = ref&.to_h
22
+ reference&.merge!(reference&.delete("ref"))
23
+
24
+ {
25
+ "type" => type.to_s,
26
+ "content" => content,
27
+ "ref" => reference,
28
+ }.compact
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Utilities
5
+ module BooleanAttributes
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ def self.extended(base)
11
+ base.extend(ClassMethods)
12
+ end
13
+
14
+ module ClassMethods
15
+ def register_boolean_attributes(attributes)
16
+ attributes.each do |attribute|
17
+ register_boolean_attribute(attribute)
18
+ end
19
+ end
20
+
21
+ def register_boolean_attribute(attribute)
22
+ attr_reader attribute
23
+
24
+ define_method("#{attribute}=") do |value|
25
+ instance_variable_set("@#{attribute}", !!value)
26
+ end
27
+
28
+ define_method("#{attribute}?") do
29
+ !!instance_variable_get("@#{attribute}")
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Utilities
5
+ module CommonFunctions
6
+ # Hash#transform_keys is not available in Ruby 2.4
7
+ # so we have to do this ourselves :(
8
+ # symbolize hash keys
9
+ def symbolize_keys(hash)
10
+ result = {}
11
+ hash.each_pair do |key, value|
12
+ result[key.to_sym] = value
13
+ end
14
+ result
15
+ end
16
+
17
+ # Hash#slice is not available in Ruby 2.4
18
+ # so we have to do this ourselves :(
19
+ # slice hash keys
20
+ def slice_keys(hash, keys)
21
+ result = {}
22
+ keys.each do |key|
23
+ result[key] = hash[key] if hash.key?(key)
24
+ end
25
+ result
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Glossarist
6
+ module Utilities
7
+ module Enum
8
+ module ClassMethods
9
+ def add_inheritable_attribute(attribute)
10
+ @inheritable_attributes ||= Set[:inheritable_attributes]
11
+ @inheritable_attributes << attribute
12
+ end
13
+
14
+ def inherited(subclass)
15
+ @inheritable_attributes.each do |inheritable_attribute|
16
+ instance_var = "@#{inheritable_attribute}"
17
+ subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
18
+ end
19
+ end
20
+
21
+ def enums
22
+ @enums ||= EnumCollection.new
23
+ end
24
+
25
+ def register_enum(name, values, options = {})
26
+ values = standardize_values(values)
27
+
28
+ enums.add(name, values, options)
29
+
30
+ add_inheritable_attribute(:enums)
31
+ register_type_accessor(name)
32
+
33
+ values.each do |value|
34
+ register_check_method(name, value)
35
+ register_set_method(name, value)
36
+ end
37
+ end
38
+
39
+ def registered_enums
40
+ enums.registered_enums
41
+ end
42
+
43
+ def valid_types(name)
44
+ enums.valid_types(name)
45
+ end
46
+
47
+ def type_options(name)
48
+ enums.type_options(name)
49
+ end
50
+
51
+ def register_type_reader(name)
52
+ define_method(name) do
53
+ if self.class.type_options(name)[:multiple]
54
+ selected_type[name].map(&:to_s)
55
+ else
56
+ selected_type[name].first&.to_s
57
+ end
58
+ end
59
+ end
60
+
61
+ def register_type_writer(name)
62
+ define_method("#{name}=") do |type|
63
+ select_type(name, type)
64
+ end
65
+ end
66
+
67
+ # Adds a reader and writer for the type name given.
68
+ def register_type_accessor(name)
69
+ register_type_reader(name)
70
+ register_type_writer(name)
71
+ end
72
+
73
+ def register_check_method(name, value)
74
+ define_method("#{value}?") do
75
+ !!selected_type[name]&.include?(value.to_sym)
76
+ end
77
+ end
78
+
79
+ def register_set_method(name, value)
80
+ define_method("#{value}=") do |input|
81
+ if input
82
+ select_type(name, value)
83
+ else
84
+ deselect_type(name, value)
85
+ end
86
+ end
87
+ end
88
+
89
+ def standardize_values(values)
90
+ if values.is_a?(Array)
91
+ values.map(&:to_sym)
92
+ else
93
+ [values.to_sym]
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Utilities
5
+ module Enum
6
+ class EnumCollection
7
+ include Enumerable
8
+
9
+ def initialize
10
+ @collection = {}
11
+ end
12
+
13
+ def add(name, values, options = {})
14
+ @collection[name] = { registered_values: values, options: options }
15
+ end
16
+
17
+ def each(&block)
18
+ if block_given?
19
+ @collection.each do |object|
20
+ block.call(object)
21
+ end
22
+ else
23
+ enum_for(:each)
24
+ end
25
+ end
26
+
27
+ def registered_enums
28
+ @collection&.keys || []
29
+ end
30
+
31
+ def valid_types(name)
32
+ @collection[name][:registered_values]
33
+ end
34
+
35
+ def type_options(name)
36
+ @collection[name][:options]
37
+ end
38
+
39
+ def [](name)
40
+ @collection[name]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Utilities
5
+ module Enum
6
+ module InstanceMethods
7
+ def selected_type
8
+ initialize_selected_type if @selected_type.nil?
9
+
10
+ @selected_type
11
+ end
12
+
13
+ def select_type(type_name, values)
14
+ values = if values.is_a?(Array)
15
+ values
16
+ else
17
+ [values]
18
+ end
19
+
20
+ values.each do |value|
21
+ select_type_value(type_name, value)
22
+ end
23
+ end
24
+
25
+ def deselect_type(type_name, value)
26
+ selected_type[type_name].delete(value)
27
+ end
28
+
29
+ private
30
+
31
+ def select_type_value(type_name, value)
32
+ if !value
33
+ selected_type[type_name].clear
34
+ elsif self.class.valid_types(type_name).include?(value.to_sym)
35
+ selected_type[type_name].clear unless self.class.type_options(type_name)[:multiple]
36
+ selected_type[type_name] << value.to_sym
37
+ else
38
+ raise(
39
+ Glossarist::InvalidTypeError,
40
+ "`#{value}` is not a valid #{type_name}. Supported #{type_name} are #{self.class.enums[type_name][:registered_values].to_a.join(", ")}"
41
+ )
42
+ end
43
+ end
44
+
45
+ def initialize_selected_type
46
+ @selected_type = {}
47
+
48
+ self.class.registered_enums.each do |type|
49
+ @selected_type[type] = []
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "enum/enum_collection"
4
+ require_relative "enum/class_methods"
5
+ require_relative "enum/instance_methods"
6
+
7
+ module Glossarist
8
+ module Utilities
9
+ module Enum
10
+ def self.included(base)
11
+ base.include(InstanceMethods)
12
+ base.extend(ClassMethods)
13
+ end
14
+
15
+ def self.extended(base)
16
+ base.include(InstanceMethods)
17
+ base.extend(ClassMethods)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "utilities/enum"
4
+ require_relative "utilities/boolean_attributes"
5
+ require_relative "utilities/common_functions"
@@ -4,5 +4,5 @@
4
4
  #
5
5
 
6
6
  module Glossarist
7
- VERSION = "0.2.0"
7
+ VERSION = "1.0.5"
8
8
  end
data/lib/glossarist.rb CHANGED
@@ -4,16 +4,41 @@
4
4
  #
5
5
 
6
6
  require "psych"
7
+ require "thor"
7
8
 
9
+ require_relative "glossarist/utilities"
8
10
  require_relative "glossarist/version"
11
+ require_relative "glossarist/glossary_definition"
9
12
 
13
+ require_relative "glossarist/asset"
10
14
  require_relative "glossarist/model"
11
- require_relative "glossarist/concept"
15
+ require_relative "glossarist/concept_date"
16
+ require_relative "glossarist/detailed_definition"
17
+ require_relative "glossarist/related_concept"
18
+ require_relative "glossarist/citation"
19
+ require_relative "glossarist/concept_set"
20
+ require_relative "glossarist/concept_source"
12
21
  require_relative "glossarist/collection"
13
- require_relative "glossarist/designations"
22
+ require_relative "glossarist/designation"
23
+ require_relative "glossarist/concept"
14
24
  require_relative "glossarist/localized_concept"
25
+ require_relative "glossarist/managed_concept_collection"
26
+ require_relative "glossarist/concept_manager"
27
+ require_relative "glossarist/managed_concept"
28
+ require_relative "glossarist/non_verb_rep"
29
+
30
+ require_relative "glossarist/collections"
31
+
32
+ require_relative "glossarist/config"
15
33
 
16
34
  module Glossarist
17
35
  class Error < StandardError; end
36
+ class InvalidTypeError < StandardError; end
18
37
  # Your code goes here...
38
+
39
+ def self.configure
40
+ config = Glossarist::Config.instance
41
+
42
+ yield(config) if block_given?
43
+ end
19
44
  end