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,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe LittleWeasel::Filters::WordFilterValidatable, type: :module do
6
+ WordFilterValidatable = described_class
7
+
8
+ class Subject
9
+ include WordFilterValidatable
10
+ end
11
+
12
+ subject { Subject.new }
13
+
14
+ let(:filter_class) do
15
+ filter = Class.new do
16
+ def filter_on?; end
17
+ def filter_off?; end
18
+ def filter_on; end
19
+ def filter_on=; end
20
+ def filter_match?; end
21
+ def self.filter_match?; end
22
+ end
23
+ filter
24
+ end
25
+
26
+ let(:numeric_filter) do
27
+ filter_class.new
28
+ end
29
+ let(:expected_error_message) { "Argument word_filter does not quack right: #{numeric_filter.class}" }
30
+
31
+ #validate_word_filter
32
+ describe '#validate_word_filter' do
33
+ context 'when argument word_filter quacks correctly' do
34
+ it 'does not raise an error' do
35
+ expect { subject.validate_word_filter(word_filter: numeric_filter) }.to_not raise_error
36
+ end
37
+ end
38
+
39
+ context 'when argument word_filter DOES NOT quack correctly to the right instance methods' do
40
+ context 'when word_filter does not respond to #filter_on?' do
41
+ before { numeric_filter.instance_eval("undef #{method_to_check}") }
42
+ let(:method_to_check) { :filter_on? }
43
+
44
+ it 'raises an error' do
45
+ expect { subject.validate_word_filter(word_filter: numeric_filter) }.to raise_error \
46
+ expected_error_message
47
+ end
48
+ end
49
+
50
+ context 'when word_filter does not respond to #filter_on' do
51
+ before { numeric_filter.instance_eval("undef #{method_to_check}") }
52
+ let(:method_to_check) { :filter_on }
53
+
54
+ it 'raises an error' do
55
+ expect { subject.validate_word_filter(word_filter: numeric_filter) }.to raise_error \
56
+ expected_error_message
57
+ end
58
+ end
59
+
60
+ context 'when word_filter does not respond to #filter_on=' do
61
+ before { numeric_filter.instance_eval("undef #{method_to_check}") }
62
+ let(:method_to_check) { :filter_on= }
63
+
64
+ it 'raises an error' do
65
+ expect { subject.validate_word_filter(word_filter: numeric_filter) }.to raise_error \
66
+ expected_error_message
67
+ end
68
+ end
69
+
70
+ context 'when word_filter does not respond to #filter_match?' do
71
+ before { numeric_filter.instance_eval("undef #{method_to_check}") }
72
+ let(:method_to_check) { :filter_match? }
73
+
74
+ it 'raises an error' do
75
+ expect { subject.validate_word_filter(word_filter: numeric_filter) }.to raise_error \
76
+ expected_error_message
77
+ end
78
+ end
79
+ end
80
+
81
+ context 'when argument word_filter DOES NOT quack correctly to the right class methods' do
82
+ before { allow(filter_class).to receive(:respond_to?).with(method_to_check).and_return(false) }
83
+
84
+ context 'when word_filter class does not respond to #filter_match?' do
85
+ let(:method_to_check) { :filter_match? }
86
+
87
+ it 'raises an error' do
88
+ expect { subject.validate_word_filter(word_filter: numeric_filter) }.to raise_error \
89
+ expected_error_message
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe LittleWeasel::Filters::WordFiltersValidatable, type: :module do
6
+ include_context 'mock word filters'
7
+
8
+ WordFiltersValidatable = described_class
9
+
10
+ class Subject
11
+ include WordFiltersValidatable
12
+ end
13
+
14
+ subject { Subject.new }
15
+
16
+ let(:word_filters) do
17
+ [
18
+ WordFilter01.new,
19
+ WordFilter02.new
20
+ ]
21
+ end
22
+ let(:expected_error_message) { "Argument word_filter does not quack right: #{numeric_filter.class}" }
23
+
24
+ #validate_word_filters
25
+ describe '#validate_word_filters' do
26
+ context 'when argument word_filters is not an Array' do
27
+ let(:word_filters) { :not_an_array}
28
+
29
+ it 'raises an error' do
30
+ expect { subject.validate_word_filters(word_filters: word_filters) }.to raise_error "Argument word_filters is not an Array: #{word_filters.class}"
31
+ end
32
+ end
33
+
34
+ context 'when argument word_filters contains valid word filters' do
35
+ it 'does not raise an error' do
36
+ expect { subject.validate_word_filters(word_filters: word_filters) }.to_not raise_error
37
+ end
38
+ end
39
+
40
+ context 'when argument word_filters contains INVALID word filters' do
41
+ let(:word_filters) { [Object.new] }
42
+
43
+ it 'does not raise an error' do
44
+ expect { subject.validate_word_filters(word_filters: word_filters) }.to raise_error "Argument word_filter does not quack right: #{word_filters[0].class}"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'Dictionary integration', type: :integration do
6
+ include_context 'dictionary keys'
7
+ include_context 'mock word preprocessors'
8
+
9
+ subject { create(:dictionary_manager) }
10
+
11
+ before(:each) { LittleWeasel.configure { |config| config.reset } }
12
+
13
+ let(:dictionary) do
14
+ subject.create_dictionary_from_file(dictionary_key: dictionary_key, file: dictionary_file_path,
15
+ word_filters: word_filters, word_preprocessors: word_preprocessors)
16
+ end
17
+ let(:dictionary_key) { dictionary_key_for(language: :en, region: :us) }
18
+ let(:dictionary_cache) { subject.dictionary_cache }
19
+ let(:dictionary_metadata) { subject.dictionary_metadata }
20
+ let(:dictionary_file_path) { dictionary_path_for(file_name: dictionary_key.key) }
21
+ let(:dictionary_words) { dictionary_words_for(dictionary_file_path: dictionary_file_path) }
22
+ let(:word_filters) {}
23
+ let(:word_preprocessors) {}
24
+
25
+ #word_results
26
+ describe '#word_results' do
27
+ describe 'when using word filters' do
28
+ let(:word_filters) { [LittleWeasel::Filters::EnUs::NumericFilter.new] }
29
+ let(:number) { 1_000.to_s }
30
+
31
+ context 'with word filters turned on' do
32
+ let(:word_results) { dictionary.word_results(number) }
33
+
34
+ it '#success? returns true' do
35
+ expect(word_results.success?).to eq true
36
+ end
37
+
38
+ it '#filter_match? returns true' do
39
+ expect(word_results.filter_match?).to eq true
40
+ end
41
+
42
+ it '#filters_matched returns the filter(s) that were matched' do
43
+ expect(word_results.filters_matched).to eq [:numeric_filter]
44
+ end
45
+
46
+ it '#word_valid? returns false' do
47
+ expect(word_results.word_valid?).to eq false
48
+ end
49
+
50
+ it '#original_word returns the original word' do
51
+ expect(word_results.preprocessed_word_or_original_word).to eq number
52
+ end
53
+
54
+ it '#preprocessed_word return nil because no word preprocessors were applied' do
55
+ expect(word_results.preprocessed_word).to be_nil
56
+ end
57
+
58
+ it '#preprocessed_word_or_original_word returns the original word' do
59
+ expect(word_results.preprocessed_word_or_original_word).to eq number
60
+ end
61
+
62
+ it '#word_cached? returns false if the word is not in the dictionary' do
63
+ expect(word_results.word_cached?).to eq false
64
+ end
65
+
66
+ it '#word_cached? returns true 2nd-nth time the word was searched if invalid words are being cached by any metadata processing' do
67
+ dictionary.word_results(number)
68
+ expect(word_results.word_cached?).to eq true
69
+ end
70
+ end
71
+
72
+ context 'with filters turned off' do
73
+ before do
74
+ dictionary.filters_on = false
75
+ end
76
+
77
+ let(:word_results) { dictionary.word_results(number) }
78
+
79
+ it '#success? returns false' do
80
+ expect(word_results.success?).to eq false
81
+ end
82
+
83
+ it '#filter_match? returns false' do
84
+ expect(word_results.filter_match?).to eq false
85
+ end
86
+
87
+ it '#filters_matched returns an empty Array' do
88
+ expect(word_results.filters_matched).to eq []
89
+ end
90
+
91
+ it '#word_valid? returns false' do
92
+ expect(word_results.word_valid?).to eq false
93
+ end
94
+
95
+ it '#original_word returns the original word' do
96
+ expect(word_results.original_word).to eq number
97
+ end
98
+
99
+ it '#preprocessed_word return nil because no word preprocessors were applied' do
100
+ expect(word_results.preprocessed_word).to be_nil
101
+ end
102
+
103
+ it '#preprocessed_word_or_original_word returns the original word' do
104
+ expect(word_results.preprocessed_word_or_original_word).to eq number
105
+ end
106
+
107
+ it '#word_cached? returns false if the word is not in the dictionary' do
108
+ expect(word_results.word_cached?).to eq false
109
+ end
110
+
111
+ it '#word_cached? returns true 2nd-nth time the word was searched if invalid words are being cached by any metadata processing' do
112
+ dictionary.word_results(number)
113
+ expect(word_results.word_cached?).to eq true
114
+ end
115
+ end
116
+ end
117
+
118
+ describe 'when using word preprocessors' do
119
+ let(:word_preprocessors) { [DowncaseWordPreprocessor.new(order: 0)] }
120
+ let(:word) { 'ApPlE' }
121
+ let(:word_results) { dictionary.word_results(word) }
122
+
123
+ context 'with preprocessors turned on' do
124
+ it '#success? returns true' do
125
+ expect(word_results.success?).to eq true
126
+ end
127
+
128
+ it '#filter_match? returns false' do
129
+ expect(word_results.filter_match?).to eq false
130
+ end
131
+
132
+ it '#filters_matched returns the filter(s) that were matched' do
133
+ expect(word_results.filters_matched).to eq []
134
+ end
135
+
136
+ it '#word_valid? returns true' do
137
+ expect(word_results.word_valid?).to eq true
138
+ end
139
+
140
+ it '#original_word returns the original word' do
141
+ expect(word_results.original_word).to eq word
142
+ end
143
+
144
+ it '#preprocessed_word returns the preprocessed word' do
145
+ expect(word_results.preprocessed_word).to eq word.downcase
146
+ end
147
+
148
+ it '#preprocessed_word_or_original_word returns the preprocessed word' do
149
+ expect(word_results.preprocessed_word_or_original_word).to eq word.downcase
150
+ end
151
+
152
+ it '#word_cached? returns true if the word is in the dictionary' do
153
+ expect(word_results.word_cached?).to eq true
154
+ end
155
+ end
156
+
157
+ context 'with preprocessors turned off' do
158
+ before do
159
+ dictionary.preprocessors_on = false
160
+ end
161
+
162
+ it '#success? returns false' do
163
+ expect(word_results.success?).to eq false
164
+ end
165
+
166
+ it '#filter_match? returns false' do
167
+ expect(word_results.filter_match?).to eq false
168
+ end
169
+
170
+ it '#filters_matched returns the filter(s) that were matched' do
171
+ expect(word_results.filters_matched).to eq []
172
+ end
173
+
174
+ it '#word_valid? returns false' do
175
+ expect(word_results.word_valid?).to eq false
176
+ end
177
+
178
+ it '#original_word returns the original word' do
179
+ expect(word_results.original_word).to eq word
180
+ end
181
+
182
+ it '#preprocessed_word returns nil' do
183
+ expect(word_results.preprocessed_word).to be_nil
184
+ end
185
+
186
+ it '#preprocessed_word_or_original_word returns the original word' do
187
+ expect(word_results.preprocessed_word_or_original_word).to eq word
188
+ end
189
+
190
+ it '#word_cached? returns false if the word is not in the dictionary' do
191
+ expect(word_results.word_cached?).to eq false
192
+ end
193
+
194
+ it '#word_cached? returns true 2nd-nth time the word was searched if invalid words are being cached by any metadata processing' do
195
+ dictionary.word_results(word)
196
+ expect(word_results.word_cached?).to eq true
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe LittleWeasel::Modules::DictionaryCreatorServicable, type: :module do
6
+ include_context 'dictionary keys'
7
+
8
+ DictionaryCreatorServicable = described_class
9
+
10
+ class SubjectMock
11
+ include DictionaryCreatorServicable
12
+ include LittleWeasel::Filters::WordFilterable
13
+ include LittleWeasel::Modules::DictionaryKeyable
14
+ include LittleWeasel::Modules::DictionaryMetadataServicable
15
+
16
+ def initialize(dictionary_key:, dictionary_cache:, dictionary_metadata:, word_filters:)
17
+ self.dictionary_key = dictionary_key
18
+ self.dictionary_cache = dictionary_cache
19
+ self.dictionary_metadata = dictionary_metadata
20
+ self.word_filters = word_filters
21
+ end
22
+ end
23
+
24
+ subject { SubjectMock.new(dictionary_key: dictionary_key, dictionary_cache: dictionary_cache, dictionary_metadata: dictionary_metadata, word_filters: word_filters) }
25
+
26
+ let(:en_us_dictionary_key) { dictionary_key_for(language: :en, region: :us) }
27
+ let(:dictionary_key) { en_us_dictionary_key }
28
+ let(:key) { dictionary_key.key }
29
+ let(:file) { dictionary_path_for(file_name: key) }
30
+ let(:dictionary_cache) { {} }
31
+ let(:dictionary_metadata) { {} }
32
+ let(:word_filters) {}
33
+
34
+ #attributes
35
+ describe 'attributes' do
36
+ it 'responds to the correct attributes' do
37
+ expect(subject).to respond_to(
38
+ :dictionary_key,
39
+ :dictionary_cache,
40
+ :word_filters)
41
+ end
42
+ end
43
+
44
+ #dictionary_creator_service
45
+ describe '#dictionary_creator_service' do
46
+ it 'responds to #dictionary_creator_service' do
47
+ expect(subject).to respond_to(:dictionary_creator_service)
48
+ end
49
+
50
+ it 'returns a Services::DictionaryCreatorService object' do
51
+ expect(subject.dictionary_creator_service).to be_kind_of LittleWeasel::Services::DictionaryCreatorService
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe LittleWeasel::Metadata::DictionaryMetadata do
6
+ subject { create(:dictionary_metadata, dictionary_words: dictionary_words, dictionary_key: dictionary_key, dictionary_cache: dictionary_cache, dictionary_metadata: dictionary_metadata) }
7
+
8
+ let!(:dictionary_cache_service) { create(:dictionary_cache_service, dictionary_key: dictionary_key, dictionary_cache: dictionary_cache, dictionary_file_source: true, load: true) }
9
+ let!(:dictionary_metadata_service) { create(:dictionary_metadata_service, dictionary_key: dictionary_key, dictionary_cache: dictionary_cache, dictionary_metadata: dictionary_metadata) }
10
+ let(:dictionary_words) { create(:dictionary_hash) }
11
+ let(:dictionary_key) { create(:dictionary_key) }
12
+ let(:dictionary_cache) { {} }
13
+ let(:dictionary_metadata) { {} }
14
+ let(:invalid_words_metadata_key) { invalid_words_metadata_class.metadata_key }
15
+ let(:invalid_words_metadata_class) { LittleWeasel::Metadata::InvalidWordsMetadata }
16
+
17
+ #.new
18
+ describe '#.new' do
19
+ context 'with valid arguments' do
20
+ it 'instantiates the object' do
21
+ expect { subject }.to_not raise_error
22
+ end
23
+ end
24
+
25
+ context 'with invalid arguments' do
26
+ context 'when dictionary_words is nil' do
27
+ # Note: do not use the factory for this spec becasue
28
+ # it creates a dictionary if a nil dictionay is passed
29
+ # so the test will never pass if using the factory.
30
+ subject { described_class.new(dictionary_words: dictionary_words, dictionary_key: dictionary_key, dictionary_cache: dictionary_cache, dictionary_metadata: dictionary_metadata) }
31
+
32
+ let(:dictionary_words) {}
33
+
34
+ it 'raises an error' do
35
+ expect { subject }.to raise_error(/Argument dictionary_words is not a Hash/)
36
+ end
37
+ end
38
+
39
+ context 'when dictionary_words is not a Hash' do
40
+ let(:dictionary_words) { %w(I am a bad dictionary) }
41
+
42
+ it 'raises an error' do
43
+ expect { subject }.to raise_error(/Argument dictionary_words is not a Hash/)
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ #[]
50
+ describe '#[]' do
51
+ context 'when passing a valid metadata_key' do
52
+ context 'when it is a key that points to a valid metadata observer' do
53
+ before do
54
+ subject.add_observers
55
+ end
56
+
57
+ it 'returns the metadata observer object' do
58
+ expect(subject[invalid_words_metadata_key]).to be_kind_of invalid_words_metadata_class
59
+ end
60
+ end
61
+
62
+ context 'when it is a key that DOES NOT point to a valid metadata observer' do
63
+ it 'returns nil' do
64
+ expect(subject[invalid_words_metadata_key]).to be_nil
65
+ end
66
+ end
67
+ end
68
+
69
+ context 'when passing an INVALID metadata_key' do
70
+ it 'returns nil' do
71
+ expect(subject[:bad_metadata_key]).to be_nil
72
+ end
73
+ end
74
+ end
75
+
76
+ #init
77
+ describe '#init' do
78
+ context 'when there are observers attached' do
79
+ before do
80
+ subject.add_observers
81
+ end
82
+
83
+ context 'when metadata is in the dictionary cache' do
84
+ it 'observers are notified to init' do
85
+ # Sanity check.
86
+ expect(subject.count_observers).to eq 1
87
+ expect(subject.observers[invalid_words_metadata_key]).to receive(:init)
88
+ subject.init
89
+ end
90
+ end
91
+ end
92
+
93
+ context 'when there are NO observers attached' do
94
+ it 'observers are NOT notified to init' do
95
+ # Sanity check.
96
+ expect(subject.count_observers).to eq 0
97
+ expect(subject.observers[invalid_words_metadata_key]).to_not receive(:init)
98
+ subject.init
99
+ end
100
+ end
101
+
102
+ context 'when dictionary metadata is already in the dictionary cache' do
103
+ it 'inits the metadata with what is currently in the dictionary cache' do
104
+ end
105
+ end
106
+ end
107
+
108
+ #refresh
109
+ describe '#refresh' do
110
+ context 'when there are observers attached' do
111
+ before do
112
+ subject.add_observers
113
+ end
114
+
115
+ it 'observers are notified to refresh' do
116
+ # Sanity check.
117
+ expect(subject.count_observers).to eq 1
118
+ expect(subject.observers[invalid_words_metadata_key]).to receive(:refresh)
119
+ subject.refresh
120
+ end
121
+ end
122
+
123
+ context 'when there are NO observers attached' do
124
+ it 'observers are NOT notified to refresh' do
125
+ # Sanity check.
126
+ expect(subject.count_observers).to eq 0
127
+ expect(subject.observers[invalid_words_metadata_key]).to_not receive(:refresh)
128
+ subject.refresh
129
+ end
130
+ end
131
+
132
+ context 'when dictionary metadata is already in the dictionary cache' do
133
+ it 'refreshes the metadata with what is currently in the dictionary cache' do
134
+ end
135
+ end
136
+ end
137
+
138
+ #delete_observer
139
+ describe '#delete_observer' do
140
+ context 'when a valid observer object is passed' do
141
+ before do
142
+ subject.add_observers
143
+ end
144
+
145
+ let(:observer) { subject.observers.values.first }
146
+
147
+ it 'the observer is removed from the #observers list' do
148
+ expect { subject.delete_observer(observer) }
149
+ .to change { subject.observers.count }
150
+ .from(1)
151
+ .to(0)
152
+ end
153
+ end
154
+
155
+ context 'when an INVALID observer object is passed' do
156
+ let(:bad_observer) { :bad_observer }
157
+
158
+ it 'raises an error' do
159
+ expect { subject.delete_observer(bad_observer) }.to raise_error 'Argument metadata_observable is not a ' \
160
+ "Metadata::MetadataObserverable object: #{bad_observer.class}"
161
+ end
162
+ end
163
+ end
164
+
165
+ #add_observers
166
+ describe '#add_observers' do
167
+ it 'returns the same object that was called' do
168
+ expect(subject.add_observers).to be subject
169
+ end
170
+
171
+ context 'when a metadata object is in a state to observe' do
172
+ it 'added as an observer' do
173
+ expect(subject.add_observers.count_observers).to eq 1
174
+ end
175
+
176
+ context 'when #add_observers is called more than once' do
177
+ context 'when argument force is true (force: true)' do
178
+ before do
179
+ subject.add_observers
180
+ end
181
+
182
+ it 'reinstantiates and replaces the observers' do
183
+ expect(subject.add_observers(force: true).count_observers).to eq 1
184
+ end
185
+ end
186
+
187
+ context 'when argument force is false (force: false)' do
188
+ before do
189
+ subject.add_observers
190
+ end
191
+
192
+ it 'raises an error' do
193
+ expect { subject.add_observers }.to raise_error 'Observers have already been added; use #add_observers(force: true) instead'
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ context 'when a metadata object is NOT in a state to observe' do
200
+ before do
201
+ allow(LittleWeasel::Metadata::InvalidWordsMetadata).to receive(:observe?).and_return(false)
202
+ end
203
+
204
+ it 'NOT added as an observer' do
205
+ expect(subject.add_observers.count_observers).to eq 0
206
+ end
207
+ end
208
+ end
209
+ end