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 CHANGED
@@ -1,5 +1,10 @@
1
1
  language: ruby
2
+
2
3
  rvm:
3
4
  - 1.9.3
4
5
  - 2.0.0
5
6
  - ruby-head
7
+
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: ruby-head
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.downcase
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, "path must be a String or File"
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, "word_list should be an array of strings" unless word_list.kind_of?(Array)
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,3 +1,3 @@
1
1
  class Dictionary
2
- VERSION = '1.0.1'
2
+ VERSION = '1.1.0'
3
3
  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, "must be a string" unless word.kind_of?(String)
18
- _append(word.strip.downcase)
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, "must be a string" unless word.kind_of?(String)
23
- _find(word.strip.downcase)
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 '#exists?' do
9
- it 'finds existing single-letter words' do
10
- subject.exists?('a').should be_true
9
+ describe '#case_sensitive?' do
10
+ describe 'when case-insensitive' do
11
+ it { should_not be_case_sensitive }
11
12
  end
12
13
 
13
- it 'finds existing multi-letter words' do
14
- subject.exists?('ab').should be_true
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
- it "doesn't find non-existing single-letter words" do
18
- subject.exists?('e').should be_false
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
- it "doesn't find non-existing multi-letter words" do
22
- subject.exists?('eggsactly').should be_false
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
- it 'finds all words starting with a single letter' do
28
- words = subject.starting_with('a')
29
- words.size.should eq 5
30
- words.should include 'a'
31
- words.should include 'absolutely'
32
- words.each { |word| word.should start_with 'a' }
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
- it 'finds all words starting with multiple letters' do
36
- words = subject.starting_with('abso')
37
- words.size.should eq 2
38
- words.should include 'absolute'
39
- words.should include 'absolutely'
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
- it 'finds no words starting with unmatched single letter' do
44
- subject.starting_with('e').should be_empty
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
- it 'finds no words starting with unmatched multiple letters' do
48
- subject.starting_with('absolutetastic').should be_empty
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
- before do
20
- subject << 'potato'
21
- end
33
+ describe 'when case-insensitive' do
34
+ before do
35
+ subject << 'potato'
36
+ end
22
37
 
23
- it 'finds existing word paths' do
24
- word_path = subject.find('potat')
25
- word_path.should be_a Dictionary::WordPath
26
- word_path.should eq Dictionary::WordPath.new.tap { |wp| wp << 'o' }
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
- it 'does not find nonexistent word paths' do
30
- subject.find('potable').should be_nil
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.find('pot').should be_nil
80
+ subject << 'potato'
37
81
  end
38
82
 
39
- it 'appends word to dictionary' do
40
- subject << 'potato'
41
- subject.find('pot').should_not be_nil
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 << 'potato'
49
- subject << 'potable'
103
+ subject << 'poTAto'
104
+ subject << 'potABle'
105
+ subject << 'POTTY'
50
106
  subject << 'pert'
51
107
  subject << 'jar'
52
108
  end
53
109
 
54
- it 'finds all words with the given suffix' do
55
- word = subject.find('pot')
56
- word.suffixes.should eq %w(ato able)
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.1
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-03-24 00:00:00.000000000 Z
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: &2157608560 !ruby/object:Gem::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: *2157608560
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: &2157608140 !ruby/object:Gem::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: *2157608140
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.10
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