LittleWeasel 3.0.4 → 4.0.0

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 (152) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.reek.yml +17 -0
  4. data/.rspec +4 -2
  5. data/.rubocop.yml +187 -0
  6. data/.ruby-version +1 -1
  7. data/.yardopts +2 -0
  8. data/Gemfile +3 -1
  9. data/LittleWeasel.gemspec +31 -18
  10. data/README.md +408 -42
  11. data/Rakefile +296 -3
  12. data/lib/LittleWeasel.rb +5 -184
  13. data/lib/LittleWeasel/block_results.rb +81 -0
  14. data/lib/LittleWeasel/configure.rb +98 -0
  15. data/lib/LittleWeasel/dictionary.rb +125 -0
  16. data/lib/LittleWeasel/dictionary_key.rb +48 -0
  17. data/lib/LittleWeasel/dictionary_manager.rb +85 -0
  18. data/lib/LittleWeasel/errors/dictionary_file_already_loaded_error.rb +9 -0
  19. data/lib/LittleWeasel/errors/dictionary_file_empty_error.rb +8 -0
  20. data/lib/LittleWeasel/errors/dictionary_file_not_found_error.rb +8 -0
  21. data/lib/LittleWeasel/errors/dictionary_file_too_large_error.rb +16 -0
  22. data/lib/LittleWeasel/errors/language_required_error.rb +8 -0
  23. data/lib/LittleWeasel/errors/must_override_error.rb +8 -0
  24. data/lib/LittleWeasel/filters/en_us/currency_filter.rb +19 -0
  25. data/lib/LittleWeasel/filters/en_us/numeric_filter.rb +19 -0
  26. data/lib/LittleWeasel/filters/en_us/single_character_word_filter.rb +21 -0
  27. data/lib/LittleWeasel/filters/word_filter.rb +59 -0
  28. data/lib/LittleWeasel/filters/word_filter_managable.rb +80 -0
  29. data/lib/LittleWeasel/filters/word_filter_validatable.rb +31 -0
  30. data/lib/LittleWeasel/filters/word_filterable.rb +19 -0
  31. data/lib/LittleWeasel/filters/word_filters_validatable.rb +29 -0
  32. data/lib/LittleWeasel/metadata/dictionary_metadata.rb +145 -0
  33. data/lib/LittleWeasel/metadata/invalid_words_metadata.rb +134 -0
  34. data/lib/LittleWeasel/metadata/invalid_words_service_results.rb +45 -0
  35. data/lib/LittleWeasel/metadata/metadata_observable_validatable.rb +22 -0
  36. data/lib/LittleWeasel/metadata/metadata_observerable.rb +90 -0
  37. data/lib/LittleWeasel/metadata/metadatable.rb +136 -0
  38. data/lib/LittleWeasel/modules/class_name_to_symbol.rb +26 -0
  39. data/lib/LittleWeasel/modules/configurable.rb +26 -0
  40. data/lib/LittleWeasel/modules/deep_dup.rb +11 -0
  41. data/lib/LittleWeasel/modules/dictionary_cache_keys.rb +34 -0
  42. data/lib/LittleWeasel/modules/dictionary_cache_servicable.rb +26 -0
  43. data/lib/LittleWeasel/modules/dictionary_cache_validatable.rb +20 -0
  44. data/lib/LittleWeasel/modules/dictionary_creator_servicable.rb +27 -0
  45. data/lib/LittleWeasel/modules/dictionary_file_loader.rb +67 -0
  46. data/lib/LittleWeasel/modules/dictionary_key_validatable.rb +19 -0
  47. data/lib/LittleWeasel/modules/dictionary_keyable.rb +24 -0
  48. data/lib/LittleWeasel/modules/dictionary_loader_servicable.rb +27 -0
  49. data/lib/LittleWeasel/modules/dictionary_metadata_servicable.rb +29 -0
  50. data/lib/LittleWeasel/modules/dictionary_metadata_validatable.rb +17 -0
  51. data/lib/LittleWeasel/modules/dictionary_sourceable.rb +26 -0
  52. data/lib/LittleWeasel/modules/dictionary_validatable.rb +30 -0
  53. data/lib/LittleWeasel/modules/language.rb +23 -0
  54. data/lib/LittleWeasel/modules/language_validatable.rb +16 -0
  55. data/lib/LittleWeasel/modules/locale.rb +40 -0
  56. data/lib/LittleWeasel/modules/order_validatable.rb +18 -0
  57. data/lib/LittleWeasel/modules/orderable.rb +17 -0
  58. data/lib/LittleWeasel/modules/region.rb +23 -0
  59. data/lib/LittleWeasel/modules/region_validatable.rb +16 -0
  60. data/lib/LittleWeasel/modules/tag_validatable.rb +16 -0
  61. data/lib/LittleWeasel/modules/taggable.rb +31 -0
  62. data/lib/LittleWeasel/modules/word_results_validatable.rb +28 -0
  63. data/lib/LittleWeasel/preprocessors/en_us/capitalize_preprocessor.rb +22 -0
  64. data/lib/LittleWeasel/preprocessors/preprocessed_word.rb +28 -0
  65. data/lib/LittleWeasel/preprocessors/preprocessed_word_validatable.rb +55 -0
  66. data/lib/LittleWeasel/preprocessors/preprocessed_words.rb +55 -0
  67. data/lib/LittleWeasel/preprocessors/preprocessed_words_validatable.rb +27 -0
  68. data/lib/LittleWeasel/preprocessors/word_preprocessable.rb +19 -0
  69. data/lib/LittleWeasel/preprocessors/word_preprocessor.rb +122 -0
  70. data/lib/LittleWeasel/preprocessors/word_preprocessor_managable.rb +114 -0
  71. data/lib/LittleWeasel/preprocessors/word_preprocessor_validatable.rb +40 -0
  72. data/lib/LittleWeasel/preprocessors/word_preprocessors_validatable.rb +24 -0
  73. data/lib/LittleWeasel/services/dictionary_cache_service.rb +262 -0
  74. data/lib/LittleWeasel/services/dictionary_creator_service.rb +94 -0
  75. data/lib/LittleWeasel/services/dictionary_file_loader_service.rb +37 -0
  76. data/lib/LittleWeasel/services/dictionary_killer_service.rb +35 -0
  77. data/lib/LittleWeasel/services/dictionary_loader_service.rb +59 -0
  78. data/lib/LittleWeasel/services/dictionary_metadata_service.rb +114 -0
  79. data/lib/LittleWeasel/services/invalid_words_service.rb +59 -0
  80. data/lib/LittleWeasel/version.rb +3 -1
  81. data/lib/LittleWeasel/word_results.rb +146 -0
  82. data/spec/factories/dictionary.rb +43 -0
  83. data/spec/factories/dictionary_cache_service.rb +95 -0
  84. data/spec/factories/dictionary_creator_service.rb +16 -0
  85. data/spec/factories/dictionary_file_loader_service.rb +13 -0
  86. data/spec/factories/dictionary_hash.rb +39 -0
  87. data/spec/factories/dictionary_key.rb +14 -0
  88. data/spec/factories/dictionary_killer_service.rb +14 -0
  89. data/spec/factories/dictionary_loader_service.rb +14 -0
  90. data/spec/factories/dictionary_manager.rb +10 -0
  91. data/spec/factories/dictionary_metadata.rb +16 -0
  92. data/spec/factories/dictionary_metadata_service.rb +16 -0
  93. data/spec/factories/numeric_filter.rb +12 -0
  94. data/spec/factories/preprocessed_word.rb +16 -0
  95. data/spec/factories/preprocessed_words.rb +41 -0
  96. data/spec/factories/single_character_word_filter.rb +12 -0
  97. data/spec/factories/word_results.rb +16 -0
  98. data/spec/lib/LittleWeasel/block_results_spec.rb +248 -0
  99. data/spec/lib/LittleWeasel/configure_spec.rb +74 -0
  100. data/spec/lib/LittleWeasel/dictionary_key_spec.rb +118 -0
  101. data/spec/lib/LittleWeasel/dictionary_manager_spec.rb +116 -0
  102. data/spec/lib/LittleWeasel/dictionary_spec.rb +289 -0
  103. data/spec/lib/LittleWeasel/filters/en_us/currency_filter_spec.rb +80 -0
  104. data/spec/lib/LittleWeasel/filters/en_us/numeric_filter_spec.rb +66 -0
  105. data/spec/lib/LittleWeasel/filters/en_us/single_character_word_filter_spec.rb +58 -0
  106. data/spec/lib/LittleWeasel/filters/word_filter_managable_spec.rb +180 -0
  107. data/spec/lib/LittleWeasel/filters/word_filter_spec.rb +151 -0
  108. data/spec/lib/LittleWeasel/filters/word_filter_validatable_spec.rb +94 -0
  109. data/spec/lib/LittleWeasel/filters/word_filters_validatable_spec.rb +48 -0
  110. data/spec/lib/LittleWeasel/integraton_tests/dictionary_integration_spec.rb +201 -0
  111. data/spec/lib/LittleWeasel/metadata/dictionary_creator_servicable_spec.rb +54 -0
  112. data/spec/lib/LittleWeasel/metadata/dictionary_metadata_spec.rb +209 -0
  113. data/spec/lib/LittleWeasel/metadata/invalid_words_metadata_spec.rb +155 -0
  114. data/spec/lib/LittleWeasel/metadata/metadata_observerable_spec.rb +31 -0
  115. data/spec/lib/LittleWeasel/metadata/metadatable_spec.rb +35 -0
  116. data/spec/lib/LittleWeasel/modules/class_name_to_symbol_spec.rb +21 -0
  117. data/spec/lib/LittleWeasel/modules/dictionary_file_loader_spec.rb +125 -0
  118. data/spec/lib/LittleWeasel/modules/dictionary_sourceable_spec.rb +44 -0
  119. data/spec/lib/LittleWeasel/modules/language_spec.rb +52 -0
  120. data/spec/lib/LittleWeasel/modules/locale_spec.rb +140 -0
  121. data/spec/lib/LittleWeasel/modules/region_spec.rb +52 -0
  122. data/spec/lib/LittleWeasel/preprocessors/en_us/capitalize_preprocessor_spec.rb +34 -0
  123. data/spec/lib/LittleWeasel/preprocessors/preprocessed_word_spec.rb +105 -0
  124. data/spec/lib/LittleWeasel/preprocessors/preprocessed_word_validatable_spec.rb +143 -0
  125. data/spec/lib/LittleWeasel/preprocessors/preprocessed_words_spec.rb +77 -0
  126. data/spec/lib/LittleWeasel/preprocessors/preprocessed_words_validatable_spec.rb +58 -0
  127. data/spec/lib/LittleWeasel/preprocessors/word_preprocessor_managable_spec.rb +216 -0
  128. data/spec/lib/LittleWeasel/preprocessors/word_preprocessor_spec.rb +175 -0
  129. data/spec/lib/LittleWeasel/preprocessors/word_preprocessor_validatable_spec.rb +109 -0
  130. data/spec/lib/LittleWeasel/preprocessors/word_preprocessors_validatable_spec.rb +49 -0
  131. data/spec/lib/LittleWeasel/services/dictionary_cache_service_spec.rb +444 -0
  132. data/spec/lib/LittleWeasel/services/dictionary_creator_service_spec.rb +119 -0
  133. data/spec/lib/LittleWeasel/services/dictionary_file_loader_service_spec.rb +71 -0
  134. data/spec/lib/LittleWeasel/services/dictionary_loader_service_spec.rb +50 -0
  135. data/spec/lib/LittleWeasel/services/dictionary_metadata_service_spec.rb +279 -0
  136. data/spec/lib/LittleWeasel/word_results_spec.rb +275 -0
  137. data/spec/lib/LittleWeasel/workflow/workflow_spec.rb +20 -0
  138. data/spec/spec_helper.rb +117 -6
  139. data/spec/support/factory_bot.rb +15 -0
  140. data/spec/support/file_helpers.rb +32 -0
  141. data/spec/support/files/empty-dictionary.txt +0 -0
  142. data/{lib/dictionary → spec/support/files/en-US-big.txt} +262156 -31488
  143. data/spec/support/files/en-US-tagged.txt +26 -0
  144. data/spec/support/files/en-US.txt +26 -0
  145. data/spec/support/files/en.txt +26 -0
  146. data/spec/support/files/es-ES.txt +27 -0
  147. data/spec/support/files/es.txt +27 -0
  148. data/spec/support/general_helpers.rb +68 -0
  149. data/spec/support/shared_contexts.rb +108 -0
  150. data/spec/support/shared_examples.rb +105 -0
  151. metadata +408 -65
  152. data/spec/checker/checker_spec.rb +0 -286
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'language'
4
+ require_relative 'region'
5
+
6
+ module LittleWeasel
7
+ module Modules
8
+ # This module provides methods to handle conversion of parts of a locale to
9
+ # their string counter parts.
10
+ module Locale
11
+ def self.included(base)
12
+ base.extend(ClassMethods)
13
+ base.include(Language)
14
+ base.include(Region)
15
+ end
16
+
17
+ # class method inclusions for convenience.
18
+ module ClassMethods
19
+ def locale(language:, region: nil)
20
+ raise ArgumentError, 'Argument language does not respond to :downcase' unless language.respond_to? :downcase
21
+
22
+ if region.present? && !region.respond_to?(:upcase)
23
+ raise ArgumentError,
24
+ 'Argument region does not respond to :upcase'
25
+ end
26
+
27
+ language = normalize_language language
28
+ return language.to_s unless region.present?
29
+
30
+ region = normalize_region region
31
+ "#{language}-#{region}"
32
+ end
33
+ end
34
+
35
+ def locale
36
+ self.class.locale language: language, region: region
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Modules
5
+ # This module provides methods to validate a value that can be used
6
+ # in sorting.
7
+ module OrderValidatable
8
+ def self.validate(order:)
9
+ raise ArgumentError, "Argument order is not an Integer: #{order.class}" unless order.is_a? Integer
10
+ raise ArgumentError, "Argument order '#{order}' is not a a number from 0-n" if order.negative?
11
+ end
12
+
13
+ def validate_order(order:)
14
+ OrderValidatable.validate order: order
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Modules
5
+ # This module provides attribtues and methods to manage objects that can be
6
+ # ordered or sorted.
7
+ module Orderable
8
+ include OrderValidatable
9
+
10
+ attr_reader :order
11
+
12
+ private
13
+
14
+ attr_writer :order
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Modules
5
+ # Provides methods for normalizing a region for a locale.
6
+ module Region
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ # class method inclusions for convenience.
12
+ module ClassMethods
13
+ def normalize_region(region)
14
+ region&.upcase
15
+ end
16
+ end
17
+
18
+ def normalize_region
19
+ self.class.normalize_region region
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Modules
5
+ # This module provides methods to validate a region.
6
+ module RegionValidatable
7
+ def self.validate(region:)
8
+ raise ArgumentError, "Argument region '#{region}' is not a Symbol." unless region.blank? || region.is_a?(Symbol)
9
+ end
10
+
11
+ def validate_region(region:)
12
+ RegionValidatable.validate region: region
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Modules
5
+ # This module provides methods to validate a tag.
6
+ module TagValidatable
7
+ def self.validate(tag:)
8
+ raise ArgumentError, "Argument tag '#{tag}' is not a Symbol." unless tag.blank? || tag.is_a?(Symbol)
9
+ end
10
+
11
+ def validate_tag(tag:)
12
+ TagValidatable.validate tag: tag
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'tag_validatable'
4
+
5
+ module LittleWeasel
6
+ module Modules
7
+ # This module provides methods to manage objects that can be tagged.
8
+ # A tag is a value that can be included as part of a DictionaryKey
9
+ # object to make it unique across locales.
10
+ #
11
+ # @examples
12
+ #
13
+ # en-US-<tag>
14
+ #
15
+ # Where <tag> = a String to make this locale unique across locales
16
+ # of the same language and region.
17
+ module Taggable
18
+ include TagValidatable
19
+
20
+ attr_reader :tag
21
+
22
+ def tagged?
23
+ tag.present?
24
+ end
25
+
26
+ private
27
+
28
+ attr_writer :tag
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Modules
5
+ # This module provides methods to validate a word results
6
+ module WordResultsValidatable
7
+ def validate_original_word
8
+ raise ArgumentError, "Argument original_word is not a String: #{original_word.class}" \
9
+ unless original_word.is_a? String
10
+ end
11
+
12
+ def validate_filters_matched
13
+ raise ArgumentError, "Argument filters_matched is not an Array: #{filters_matched.class}" \
14
+ unless filters_matched.is_a? Array
15
+ end
16
+
17
+ def validate_word_cached
18
+ raise ArgumentError, "Argument word_cached is not true or false: #{word_cached.class}" \
19
+ unless [true, false].include? word_cached
20
+ end
21
+
22
+ def vaidate_word_valid
23
+ raise ArgumentError, "Argument word_valid is not true or false: #{word_cached.class}" \
24
+ unless [true, false].include? word_valid
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../word_preprocessor'
4
+
5
+ module LittleWeasel
6
+ module Preprocessors
7
+ module EnUs
8
+ # This preprocessor capitializes a word.
9
+ class CapitalizePreprocessor < WordPreprocessor
10
+ def initialize(order: 0)
11
+ super
12
+ end
13
+
14
+ class << self
15
+ def preprocess(word)
16
+ [true, word.capitalize]
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Preprocessors
5
+ # This class represents a word that has passed through
6
+ # Preprocessor::WordPreprocessor processing. Word preprocessors
7
+ # are used to preprocess a word before it is passed to any
8
+ # Filters::WordFilters, and before it is compared against the
9
+ # dictionary for validity.
10
+ class PreprocessedWord
11
+ attr_accessor :original_word, :preprocessed, :preprocessed_word, :preprocessor, :preprocessor_order
12
+
13
+ def initialize(original_word:, preprocessed:, preprocessed_word:, preprocessor:, preprocessor_order:)
14
+ self.original_word = original_word
15
+ self.preprocessed_word = preprocessed_word
16
+ self.preprocessed = preprocessed
17
+ self.preprocessor = preprocessor
18
+ self.preprocessor_order = preprocessor_order
19
+ end
20
+
21
+ # Returns true if the word was preprocessed; false, if the word
22
+ # was not preprocessed by this preprocessor.
23
+ def preprocessed?
24
+ preprocessed
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Preprocessors
5
+ # This module provides functionality that validates preprocessed word types.
6
+ # rubocop: disable Layout/LineLength
7
+ module PreprocessedWordValidatable
8
+ module_function
9
+
10
+ def validate_prepreprocessed_word(preprocessed_word:)
11
+ validate_original_word preprocessed_word: preprocessed_word
12
+ validate_preprocessed_word preprocessed_word: preprocessed_word
13
+ validate_preprocessed preprocessed_word: preprocessed_word
14
+ validate_preprocessor preprocessed_word: preprocessed_word
15
+ validate_preprocessor_order preprocessed_word: preprocessed_word
16
+ end
17
+
18
+ def validation_error_message(object:, respond_to:)
19
+ "Argument preprocessed_word: does not respond to: #{object}#{respond_to}"
20
+ end
21
+
22
+ def validate_original_word(preprocessed_word:)
23
+ preprocessed_word_class = preprocessed_word.class
24
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#original_word') unless preprocessed_word.respond_to?(:original_word)
25
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#original_word=') unless preprocessed_word.respond_to?(:original_word=)
26
+ end
27
+
28
+ def validate_preprocessed_word(preprocessed_word:)
29
+ preprocessed_word_class = preprocessed_word.class
30
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessed_word') unless preprocessed_word.respond_to?(:preprocessed_word)
31
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessed_word=') unless preprocessed_word.respond_to?(:preprocessed_word=)
32
+ end
33
+
34
+ def validate_preprocessed(preprocessed_word:)
35
+ preprocessed_word_class = preprocessed_word.class
36
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessed') unless preprocessed_word.respond_to?(:preprocessed)
37
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessed=') unless preprocessed_word.respond_to?(:preprocessed=)
38
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessed?') unless preprocessed_word.respond_to?(:preprocessed?)
39
+ end
40
+
41
+ def validate_preprocessor(preprocessed_word:)
42
+ preprocessed_word_class = preprocessed_word.class
43
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessor') unless preprocessed_word.respond_to?(:preprocessor)
44
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessor=') unless preprocessed_word.respond_to?(:preprocessor=)
45
+ end
46
+
47
+ def validate_preprocessor_order(preprocessed_word:)
48
+ preprocessed_word_class = preprocessed_word.class
49
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessor_order') unless preprocessed_word.respond_to?(:preprocessor_order)
50
+ raise validation_error_message(object: preprocessed_word_class, respond_to: '#preprocessor_order=') unless preprocessed_word.respond_to?(:preprocessor_order=)
51
+ end
52
+ end
53
+ # rubocop: enable Layout/LineLength
54
+ end
55
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LittleWeasel
4
+ module Preprocessors
5
+ # This class provides a container for Preprocessors::PreprocessedWord
6
+ # objects.
7
+ class PreprocessedWords
8
+ attr_reader :original_word
9
+ attr_accessor :preprocessed_words
10
+
11
+ # original_word:String the unsullied word before any preprocessing has
12
+ # been applied to it.
13
+ # preprocessed_words:Array, Preprocessors::PreprocessedWord, an Array
14
+ # of Preprocessors::PreprocessedWord objects that represents the
15
+ # original_word having passed through each successive
16
+ # Preprocessors::WordPreprocessor.
17
+ def initialize(original_word:, preprocessed_words:)
18
+ self.original_word = original_word
19
+ self.preprocessed_words = preprocessed_words
20
+ end
21
+
22
+ class << self
23
+ # Returns true if the word was passed through any preprocessing. If
24
+ # this is the case, #preprocessed_word may be different than
25
+ # #original_word.
26
+ def preprocessed?(preprocessed_words:)
27
+ # TODO: Do we need to check for preprocessors where
28
+ # #preprocessed? is true? or does preprocessed_words
29
+ # contain only preprocessed word objects where
30
+ # #preprocessed? is true?
31
+ preprocessed_words.present?
32
+ end
33
+
34
+ def preprocessed_word(preprocessed_words:)
35
+ return unless preprocessed? preprocessed_words: preprocessed_words
36
+
37
+ preprocessed_words.max_by(&:preprocessor_order).preprocessed_word
38
+ end
39
+ end
40
+
41
+ def preprocessed_word
42
+ self.class.preprocessed_word preprocessed_words: preprocessed_words
43
+ end
44
+
45
+ # Returns true if the word was preprocessed
46
+ def preprocessed?
47
+ self.class.preprocessed? preprocessed_words: preprocessed_words
48
+ end
49
+
50
+ private
51
+
52
+ attr_writer :original_word
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'preprocessed_word_validatable'
4
+
5
+ module LittleWeasel
6
+ module Preprocessors
7
+ # This module provides methods to validate preprocessed words types.
8
+ # rubocop: disable Layout/LineLength
9
+ module PreprocessedWordsValidatable
10
+ module_function
11
+
12
+ def validate_prepreprocessed_words(preprocessed_words:)
13
+ raise ArgumentError, validation_error_message(object: preprocessed_words, respond_to: :original_word) unless preprocessed_words.respond_to? :original_word
14
+ raise ArgumentError, validation_error_message(object: preprocessed_words, respond_to: :preprocessed_words) unless preprocessed_words.respond_to? :preprocessed_words
15
+
16
+ preprocessed_words&.preprocessed_words&.each do |preprocessed_word|
17
+ PreprocessedWordValidatable.validate_prepreprocessed_word preprocessed_word: preprocessed_word
18
+ end
19
+ end
20
+
21
+ def validation_error_message(object:, respond_to:)
22
+ "Argument preprocessed_words does not respond to: #{object.class}##{respond_to}"
23
+ end
24
+ end
25
+ # rubocop: enable Layout/LineLength
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'word_preprocessors_validatable'
4
+
5
+ module LittleWeasel
6
+ module Preprocessors
7
+ # This module provides the word_preprocessors attribute for objects
8
+ # that support word preprocessors.
9
+ module WordPreprocessable
10
+ @word_preprocessors = []
11
+
12
+ attr_reader :word_preprocessors
13
+
14
+ private
15
+
16
+ attr_writer :word_preprocessors
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../errors/must_override_error'
4
+ require_relative '../modules/class_name_to_symbol'
5
+ require_relative '../modules/orderable'
6
+ require_relative 'preprocessed_word'
7
+
8
+ module LittleWeasel
9
+ module Preprocessors
10
+ # This is a base class that provides methods and functionality for
11
+ # word preprocessors. A "word preprocessor" is an object that manipulates a
12
+ # word before it is passed to any word filters and before it is compared
13
+ # against the dictionary for validity.
14
+ class WordPreprocessor
15
+ include Modules::ClassNameToSymbol
16
+ include Modules::Orderable
17
+
18
+ attr_reader :preprocessor_on
19
+
20
+ # order:Integer, the order in which this preprocessor should
21
+ # be applied.
22
+ # preprocessor_on:Boolean, whether or not this preprocessor
23
+ # should be applied to any words.
24
+ def initialize(order:)
25
+ validate_order order: order
26
+ self.order = order
27
+ preprocessor_on!
28
+ end
29
+
30
+ class << self
31
+ # Should return true if word matches the preprocess criteria;
32
+ # false, otherwise. If this preprocessor has no preprocess criteria,
33
+ # simply return true. This class method is unlike the instance method in
34
+ # that it does not consider whether or not this preprocessor is "on"
35
+ # or "off"; it simply returns true or false based on whether or not the
36
+ # word matches the preprocess criteria.
37
+ def preprocess?(_word)
38
+ true
39
+ end
40
+
41
+ # This method should UNconditionally apply preprocessing to word ONLY if
42
+ # word meets the criteria for preprocessing (.preprocess?).
43
+ #
44
+ # This method should return the following Array:
45
+ #
46
+ # [<preprocessed?>, <preprocessed word | nil>]
47
+ #
48
+ # Where:
49
+ #
50
+ # <preprocessed?> == whether or not the word was preprocessed
51
+ # based on whether or not the word meets the preprocessing
52
+ # criteria (.preprocess?).
53
+ #
54
+ # <preprocessed word | nil> == the preprocessed word (if word
55
+ # met the preprocessing criteria (.preprocessed?)) or nil if
56
+ # word was NOT preprocessed (word did NOT meet the preprocessing
57
+ # criteria).
58
+ def preprocess(_word)
59
+ raise Errors::MustOverrideError
60
+ end
61
+ end
62
+
63
+ def preprocessor_on=(value)
64
+ raise ArgumentError, "Argument value is not true or false: #{value}" \
65
+ unless [true, false].include? value
66
+
67
+ @preprocessor_on = value
68
+ end
69
+
70
+ # Returns true if word meets the criteria for preprocessing. false
71
+ # is returned if word does not meet the criteria for preprocessing, or,
72
+ # if the preprocessor is "off".
73
+ def preprocess?(word)
74
+ return false if preprocessor_off?
75
+
76
+ self.class.preprocess? word
77
+ end
78
+
79
+ # Applies preprocessing to word if this preprocessor is "on" AND if word
80
+ # meets the criteria for preprocessing; no preprocessing is applied to
81
+ # word otherwise.
82
+ #
83
+ # This method should return a Preprocessors::PreprocessedWord object.
84
+ def preprocess(word)
85
+ preprocessed, preprocessed_word = if preprocessor_on?
86
+ self.class.preprocess word
87
+ else
88
+ [false, nil]
89
+ end
90
+ preprocessed_word(original_word: word, preprocessed_word: preprocessed_word, preprocessed: preprocessed)
91
+ end
92
+
93
+ # Returns true if this preprocessor is "on"; false, otherwise. If this
94
+ # preprocessor is "on", preprocessing should be applied to a word if word
95
+ # meets the criteria for preprocessing.
96
+ def preprocessor_on?
97
+ preprocessor_on
98
+ end
99
+
100
+ def preprocessor_on!
101
+ @preprocessor_on = true
102
+ end
103
+
104
+ # Returns true if this preprocessor is "off". Preprocessing should not
105
+ # be applied to a word if this preprocessor is "off".
106
+ def preprocessor_off?
107
+ !preprocessor_on?
108
+ end
109
+
110
+ def preprocessor_off!
111
+ @preprocessor_on = false
112
+ end
113
+
114
+ private
115
+
116
+ def preprocessed_word(original_word:, preprocessed:, preprocessed_word:)
117
+ PreprocessedWord.new(original_word: original_word, preprocessed: preprocessed,
118
+ preprocessed_word: preprocessed_word, preprocessor: to_sym, preprocessor_order: order)
119
+ end
120
+ end
121
+ end
122
+ end