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,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