ruby-dictionary 1.0.1 → 1.1.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.
- data/.travis.yml +5 -0
- data/README.md +26 -0
- data/lib/ruby-dictionary/dictionary.rb +15 -9
- data/lib/ruby-dictionary/version.rb +1 -1
- data/lib/ruby-dictionary/word_path.rb +14 -7
- data/spec/ruby-dictionary/dictionary_spec.rb +114 -29
- data/spec/ruby-dictionary/word_path_spec.rb +86 -19
- metadata +23 -7
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -53,6 +53,32 @@ the provided string.
|
|
53
53
|
dictionary.starting_with('bee') # => ["bee", "been", "bees"]
|
54
54
|
dictionary.starting_with('foo') # => []
|
55
55
|
|
56
|
+
### Case Sensitivity
|
57
|
+
|
58
|
+
By default, a new `Dictionary` is case-insensitive, meaning "bee", "Bee", and
|
59
|
+
"BEE" are all considered to be the same, regardless of adding to the dictionary
|
60
|
+
or searching within it.
|
61
|
+
|
62
|
+
However, you can choose to use case-sensitive dictionary by passing an optional
|
63
|
+
`true` parameter to both the `#new` and `#from_file` methods.
|
64
|
+
|
65
|
+
dictionary = Dictionary.new(%w(Alpha Beta), true)
|
66
|
+
dictionary.exists?('Alpha') # => true
|
67
|
+
dictionary.exists?('alpha') # => false
|
68
|
+
|
69
|
+
dictionary = Dictionary.from_file('restaurants.txt', "\n", true)
|
70
|
+
dictionary.starting_with('Mc') # => ["McDonald's"]
|
71
|
+
dictionary.starting_with('mc') # => []
|
72
|
+
|
73
|
+
Additionally, you can determine whether a dictionary is case-sensitive via the
|
74
|
+
`#case_sensitive?` method.
|
75
|
+
|
76
|
+
dictionary = Dictionary.new([])
|
77
|
+
dictionary.case_sensitive? # => false
|
78
|
+
|
79
|
+
dictionary = Dictionary.new([], true)
|
80
|
+
dictionary.case_sensitive? # => true
|
81
|
+
|
56
82
|
## Contributing
|
57
83
|
|
58
84
|
1. Fork it
|
@@ -4,8 +4,12 @@ require 'zlib'
|
|
4
4
|
require 'ruby-dictionary/word_path'
|
5
5
|
|
6
6
|
class Dictionary
|
7
|
-
def initialize(word_list)
|
8
|
-
@word_path = parse_words(word_list)
|
7
|
+
def initialize(word_list, case_sensitive = false)
|
8
|
+
@word_path = parse_words(word_list, case_sensitive)
|
9
|
+
end
|
10
|
+
|
11
|
+
def case_sensitive?
|
12
|
+
@word_path.case_sensitive?
|
9
13
|
end
|
10
14
|
|
11
15
|
def exists?(word)
|
@@ -14,7 +18,9 @@ class Dictionary
|
|
14
18
|
end
|
15
19
|
|
16
20
|
def starting_with(prefix)
|
17
|
-
prefix = prefix.to_s.strip
|
21
|
+
prefix = prefix.to_s.strip
|
22
|
+
prefix = prefix.downcase unless case_sensitive?
|
23
|
+
|
18
24
|
path = word_path(prefix)
|
19
25
|
return [] if path.nil?
|
20
26
|
|
@@ -42,11 +48,11 @@ class Dictionary
|
|
42
48
|
inspect
|
43
49
|
end
|
44
50
|
|
45
|
-
def self.from_file(path, separator = "\n")
|
51
|
+
def self.from_file(path, separator = "\n", case_sensitive = false)
|
46
52
|
contents = case path
|
47
53
|
when String then File.read(path)
|
48
54
|
when File then path.read
|
49
|
-
else raise ArgumentError,
|
55
|
+
else raise ArgumentError, 'path must be a String or File'
|
50
56
|
end
|
51
57
|
|
52
58
|
if contents.start_with?("\x1F\x8B")
|
@@ -54,15 +60,15 @@ class Dictionary
|
|
54
60
|
contents = gz.read
|
55
61
|
end
|
56
62
|
|
57
|
-
new(contents.split(separator))
|
63
|
+
new(contents.split(separator), case_sensitive)
|
58
64
|
end
|
59
65
|
|
60
66
|
private
|
61
67
|
|
62
|
-
def parse_words(word_list)
|
63
|
-
raise ArgumentError,
|
68
|
+
def parse_words(word_list, case_sensitive)
|
69
|
+
raise ArgumentError, 'word_list should be an array of strings' unless word_list.kind_of?(Array)
|
64
70
|
|
65
|
-
WordPath.new.tap do |word_path|
|
71
|
+
WordPath.new(case_sensitive).tap do |word_path|
|
66
72
|
word_list.each { |word| word_path << word.to_s }
|
67
73
|
end
|
68
74
|
end
|
@@ -1,10 +1,15 @@
|
|
1
1
|
class Dictionary
|
2
2
|
class WordPath
|
3
|
-
def initialize
|
3
|
+
def initialize(case_sensitive)
|
4
|
+
@case_sensitive = !!case_sensitive
|
4
5
|
@is_leaf = false
|
5
6
|
@word_paths = {}
|
6
7
|
end
|
7
8
|
|
9
|
+
def case_sensitive?
|
10
|
+
@case_sensitive
|
11
|
+
end
|
12
|
+
|
8
13
|
def leaf?
|
9
14
|
@is_leaf
|
10
15
|
end
|
@@ -14,13 +19,15 @@ class Dictionary
|
|
14
19
|
end
|
15
20
|
|
16
21
|
def <<(word)
|
17
|
-
raise ArgumentError,
|
18
|
-
|
22
|
+
raise ArgumentError, 'must be a string' unless word.kind_of?(String)
|
23
|
+
word = word.downcase unless @case_sensitive
|
24
|
+
_append(word.strip)
|
19
25
|
end
|
20
26
|
|
21
27
|
def find(word)
|
22
|
-
raise ArgumentError,
|
23
|
-
|
28
|
+
raise ArgumentError, 'must be a string' unless word.kind_of?(String)
|
29
|
+
word = word.downcase unless @case_sensitive
|
30
|
+
_find(word.strip)
|
24
31
|
end
|
25
32
|
|
26
33
|
def suffixes
|
@@ -33,7 +40,7 @@ class Dictionary
|
|
33
40
|
end
|
34
41
|
|
35
42
|
def hash
|
36
|
-
self.class.hash ^ @is_leaf.hash ^ @word_paths.hash
|
43
|
+
self.class.hash ^ @is_leaf.hash ^ @word_paths.hash ^ @case_sensitive.hash
|
37
44
|
end
|
38
45
|
|
39
46
|
def ==(obj)
|
@@ -65,7 +72,7 @@ class Dictionary
|
|
65
72
|
return if word.empty?
|
66
73
|
|
67
74
|
char = word[0]
|
68
|
-
word_path = @word_paths[char] ||= self.class.new
|
75
|
+
word_path = @word_paths[char] ||= self.class.new(@case_sensitive)
|
69
76
|
|
70
77
|
if word.size == 1
|
71
78
|
word_path.leaf = true
|
@@ -1,51 +1,126 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Dictionary do
|
4
|
-
subject { Dictionary.new(word_list) }
|
4
|
+
subject { Dictionary.new(word_list, case_sensitive) }
|
5
5
|
|
6
6
|
let(:word_list) { %w(a ab abs absolute absolutely be bee bees been zoo zoos zebra yuck) }
|
7
|
+
let(:case_sensitive) { false }
|
7
8
|
|
8
|
-
describe '#
|
9
|
-
|
10
|
-
|
9
|
+
describe '#case_sensitive?' do
|
10
|
+
describe 'when case-insensitive' do
|
11
|
+
it { should_not be_case_sensitive }
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
describe 'when case-sensitive' do
|
15
|
+
let(:case_sensitive) { true }
|
16
|
+
|
17
|
+
it { should be_case_sensitive }
|
15
18
|
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#exists?' do
|
22
|
+
describe 'when case-insensitive' do
|
23
|
+
it 'finds existing single-letter words regardless of casing' do
|
24
|
+
subject.exists?('a').should be_true
|
25
|
+
subject.exists?('A').should be_true
|
26
|
+
end
|
16
27
|
|
17
|
-
|
18
|
-
|
28
|
+
it 'finds existing multi-letter words regardless of casing' do
|
29
|
+
subject.exists?('ab').should be_true
|
30
|
+
subject.exists?('AB').should be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
it "doesn't find non-existing single-letter words regardless of casing" do
|
34
|
+
subject.exists?('e').should be_false
|
35
|
+
subject.exists?('E').should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it "doesn't find non-existing multi-letter words regardless of casing" do
|
39
|
+
subject.exists?('eggsactly').should be_false
|
40
|
+
subject.exists?('EGGSACTLY').should be_false
|
41
|
+
end
|
19
42
|
end
|
20
43
|
|
21
|
-
|
22
|
-
|
44
|
+
describe 'when case-sensitive' do
|
45
|
+
let(:case_sensitive) { true }
|
46
|
+
|
47
|
+
it 'finds existing single-letter words of the same casing' do
|
48
|
+
subject.exists?('a').should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'finds existing multi-letter words of the same casing' do
|
52
|
+
subject.exists?('ab').should be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "doesn't find existing single-letter words of a different casing" do
|
56
|
+
subject.exists?('A').should be_false
|
57
|
+
end
|
58
|
+
|
59
|
+
it "doesn't find existing multi-letter words of a different casing" do
|
60
|
+
subject.exists?('AB').should be_false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "doesn't find non-existing single-letter words of any casing" do
|
64
|
+
subject.exists?('e').should be_false
|
65
|
+
subject.exists?('E').should be_false
|
66
|
+
end
|
67
|
+
|
68
|
+
it "doesn't find non-existing multi-letter words of any casing" do
|
69
|
+
subject.exists?('eggsactly').should be_false
|
70
|
+
subject.exists?('EGGSACTLY').should be_false
|
71
|
+
end
|
23
72
|
end
|
24
73
|
end
|
25
74
|
|
26
75
|
describe '#starting_with' do
|
27
|
-
|
28
|
-
words
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
76
|
+
describe 'when case-insensitive' do
|
77
|
+
it 'finds all words starting with a single letter regardless of casing' do
|
78
|
+
%w(a A).each do |prefix|
|
79
|
+
subject.starting_with(prefix).should eq %w(a ab abs absolute absolutely)
|
80
|
+
end
|
81
|
+
end
|
34
82
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
words.each { |word| word.should start_with 'a' }
|
41
|
-
end
|
83
|
+
it 'finds all words starting with multiple letters regardless of casing' do
|
84
|
+
%w(abso ABSO).each do |prefix|
|
85
|
+
subject.starting_with(prefix).should eq %w(absolute absolutely)
|
86
|
+
end
|
87
|
+
end
|
42
88
|
|
43
|
-
|
44
|
-
|
89
|
+
it 'finds no words starting with unmatched single letter regardless of casing' do
|
90
|
+
%w(e E).each do |prefix|
|
91
|
+
subject.starting_with(prefix).should be_empty
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'finds no words starting with unmatched multiple letters regardless of casing' do
|
96
|
+
%w(absolutetastic ABSOLUTETASTIC).each do |prefix|
|
97
|
+
subject.starting_with(prefix).should be_empty
|
98
|
+
end
|
99
|
+
end
|
45
100
|
end
|
46
101
|
|
47
|
-
|
48
|
-
|
102
|
+
describe 'when case-sensitive' do
|
103
|
+
let(:case_sensitive) { true }
|
104
|
+
|
105
|
+
it 'finds all words starting with a single letter of the same casing' do
|
106
|
+
subject.starting_with('a').should eq %w(a ab abs absolute absolutely)
|
107
|
+
subject.starting_with('A').should be_empty
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'finds all words starting with multiple letters of the same casing' do
|
111
|
+
subject.starting_with('abso').should eq %w(absolute absolutely)
|
112
|
+
subject.starting_with('ABSO').should be_empty
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'finds no words starting with unmatched single letter of the same casing' do
|
116
|
+
subject.starting_with('e').should be_empty
|
117
|
+
subject.starting_with('E').should be_empty
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'finds no words starting with unmatched multiple letters of the same casing' do
|
121
|
+
subject.starting_with('absolutetastic').should be_empty
|
122
|
+
subject.starting_with('ABSOLUTETASTIC').should be_empty
|
123
|
+
end
|
49
124
|
end
|
50
125
|
end
|
51
126
|
|
@@ -58,7 +133,7 @@ describe Dictionary do
|
|
58
133
|
end
|
59
134
|
|
60
135
|
describe '.from_file' do
|
61
|
-
subject { Dictionary.from_file(dictionary_path, separator) }
|
136
|
+
subject { Dictionary.from_file(dictionary_path, separator, case_sensitive) }
|
62
137
|
|
63
138
|
let(:separator) { "\n" }
|
64
139
|
|
@@ -75,6 +150,16 @@ describe Dictionary do
|
|
75
150
|
subject.starting_with(letter).should be_empty
|
76
151
|
end
|
77
152
|
end
|
153
|
+
|
154
|
+
describe 'when case-insensitive' do
|
155
|
+
let(:case_sensitive) { false }
|
156
|
+
it { should_not be_case_sensitive }
|
157
|
+
end
|
158
|
+
|
159
|
+
describe 'when case-sensitive' do
|
160
|
+
let(:case_sensitive) { true }
|
161
|
+
it { should be_case_sensitive }
|
162
|
+
end
|
78
163
|
end
|
79
164
|
|
80
165
|
describe 'with compressed file' do
|
@@ -1,7 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Dictionary::WordPath do
|
4
|
-
subject { Dictionary::WordPath.new }
|
4
|
+
subject { Dictionary::WordPath.new(case_sensitive) }
|
5
|
+
|
6
|
+
let(:case_sensitive) { false }
|
7
|
+
|
8
|
+
describe '#case_sensitive?' do
|
9
|
+
describe 'when case-insensitive' do
|
10
|
+
it { should_not be_case_sensitive }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'when case-sensitive' do
|
14
|
+
let(:case_sensitive) { true }
|
15
|
+
|
16
|
+
it { should be_case_sensitive }
|
17
|
+
end
|
18
|
+
end
|
5
19
|
|
6
20
|
describe '#leaf=' do
|
7
21
|
it 'should set to false' do
|
@@ -16,44 +30,97 @@ describe Dictionary::WordPath do
|
|
16
30
|
end
|
17
31
|
|
18
32
|
describe '#find' do
|
19
|
-
|
20
|
-
|
21
|
-
|
33
|
+
describe 'when case-insensitive' do
|
34
|
+
before do
|
35
|
+
subject << 'potato'
|
36
|
+
end
|
22
37
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
38
|
+
it 'finds existing word paths of matching case-sensitivity' do
|
39
|
+
word_path = subject.find('potat')
|
40
|
+
word_path.should be_a Dictionary::WordPath
|
41
|
+
word_path.should eq Dictionary::WordPath.new(case_sensitive).tap { |wp| wp << 'o' }
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'finds existing word paths of unmatching case-sensitivity' do
|
45
|
+
word_path = subject.find('poTAt')
|
46
|
+
word_path.should be_a Dictionary::WordPath
|
47
|
+
word_path.should eq Dictionary::WordPath.new(case_sensitive).tap { |wp| wp << 'o' }
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not find nonexistent word paths' do
|
51
|
+
subject.find('potable').should be_nil
|
52
|
+
end
|
27
53
|
end
|
28
54
|
|
29
|
-
|
30
|
-
|
55
|
+
describe 'when case-sensitive' do
|
56
|
+
before do
|
57
|
+
subject << 'poTAto'
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:case_sensitive) { true }
|
61
|
+
|
62
|
+
it 'finds existing word paths of matching case-sensitivity' do
|
63
|
+
word_path = subject.find('poTAt')
|
64
|
+
word_path.should be_a Dictionary::WordPath
|
65
|
+
word_path.should eq Dictionary::WordPath.new(case_sensitive).tap { |wp| wp << 'o' }
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'does not find existing word paths of unmatching case-sensitivity' do
|
69
|
+
subject.find('potat').should be_nil
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'does not find nonexistent word paths' do
|
73
|
+
subject.find('potat').should be_nil
|
74
|
+
end
|
31
75
|
end
|
32
76
|
end
|
33
77
|
|
34
78
|
describe '#<<' do
|
35
79
|
before do
|
36
|
-
subject
|
80
|
+
subject << 'potato'
|
37
81
|
end
|
38
82
|
|
39
|
-
|
40
|
-
|
41
|
-
|
83
|
+
describe 'when case-insensitive' do
|
84
|
+
it 'appends case-insensitive word to word path' do
|
85
|
+
subject.find('potato').should be_a Dictionary::WordPath
|
86
|
+
subject.find('poTAto').should eq subject.find('potato')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'when case-sensitive' do
|
91
|
+
let(:case_sensitive) { true }
|
92
|
+
|
93
|
+
it 'appends case-sensitive word to word path' do
|
94
|
+
subject.find('potato').should be_a Dictionary::WordPath
|
95
|
+
subject.find('poTAto').should be_nil
|
96
|
+
end
|
42
97
|
end
|
43
98
|
end
|
44
99
|
|
45
100
|
describe '#suffixes' do
|
46
101
|
before do
|
47
102
|
subject << 'pot'
|
48
|
-
subject << '
|
49
|
-
subject << '
|
103
|
+
subject << 'poTAto'
|
104
|
+
subject << 'potABle'
|
105
|
+
subject << 'POTTY'
|
50
106
|
subject << 'pert'
|
51
107
|
subject << 'jar'
|
52
108
|
end
|
53
109
|
|
54
|
-
|
55
|
-
|
56
|
-
|
110
|
+
describe 'when case-insensitive' do
|
111
|
+
it 'finds all words with the given suffix' do
|
112
|
+
word = subject.find('pot')
|
113
|
+
word.suffixes.should eq %w(ato able ty)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'when case-sensitive' do
|
118
|
+
let(:case_sensitive) { true }
|
119
|
+
|
120
|
+
it 'finds all words with the given suffix' do
|
121
|
+
word = subject.find('pot')
|
122
|
+
word.suffixes.should eq %w(ABle)
|
123
|
+
end
|
57
124
|
end
|
58
125
|
end
|
59
126
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-dictionary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-07-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: rspec
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,7 +37,12 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
description: ! "Dictionary class for ruby that allows for checking\n existence
|
37
47
|
and finding words starting with a given\n prefix."
|
38
48
|
email:
|
@@ -73,15 +83,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
73
83
|
- - ! '>='
|
74
84
|
- !ruby/object:Gem::Version
|
75
85
|
version: '0'
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
hash: -3725738245159260742
|
76
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
90
|
none: false
|
78
91
|
requirements:
|
79
92
|
- - ! '>='
|
80
93
|
- !ruby/object:Gem::Version
|
81
94
|
version: '0'
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
hash: -3725738245159260742
|
82
98
|
requirements: []
|
83
99
|
rubyforge_project:
|
84
|
-
rubygems_version: 1.8.
|
100
|
+
rubygems_version: 1.8.25
|
85
101
|
signing_key:
|
86
102
|
specification_version: 3
|
87
103
|
summary: Simple dictionary class for checking existence of words
|