rambling-trie 0.4.0 → 0.4.1

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/.gitignore CHANGED
@@ -13,3 +13,7 @@ coverage/
13
13
 
14
14
  # RubyProf
15
15
  reports/
16
+
17
+ # Bundler
18
+ .bundle
19
+ pkg
data/README.markdown CHANGED
@@ -28,31 +28,37 @@ gem 'rambling-trie'
28
28
 
29
29
  ## How to use the Rambling Trie
30
30
 
31
+ ### Deprecation warning
32
+
33
+ Starting from version 0.4.0, `Rambling::Trie.new` is deprecated. Please use `Rambling::Trie.create` instead.
34
+
31
35
  To create the trie, initialize it like this:
32
36
 
33
37
  ``` ruby
34
- trie = Rambling::Trie.new
38
+ trie = Rambling::Trie.create
35
39
  ```
36
40
 
37
41
  You can also provide a file which contains all the words to be added to the trie, and it will read the file and create the structure for you, like this:
38
42
 
39
43
  ``` ruby
40
- trie = Rambling::Trie.new '/path/to/file'
44
+ trie = Rambling::Trie.create '/path/to/file'
41
45
  ```
42
46
 
43
- To add new words to the trie, use `add_branch_from`:
47
+ To add new words to the trie, use `add_branch_from` or `<<`:
44
48
 
45
49
  ``` ruby
46
50
  trie.add_branch_from 'word'
51
+ trie << 'word'
47
52
  ```
48
53
 
49
- And to find out if a word already exists in the trie, use `is_word?`:
54
+ And to find out if a word already exists in the trie, use `is_word?` or `include?`:
50
55
 
51
56
  ``` ruby
52
57
  trie.is_word? 'word'
58
+ trie.include? 'word'
53
59
  ```
54
60
 
55
- If you wish to find if part of a word exists in the `Rambling::Trie` instance, you should call `has_branch_for?`:
61
+ If you wish to find if part of a word exists in the trie instance, you should call `has_branch_for?`:
56
62
 
57
63
  ``` ruby
58
64
  trie.has_branch_for? 'partial_word'
@@ -62,7 +68,7 @@ trie.has_branch_for? 'partial_word'
62
68
 
63
69
  By default, the Rambling Trie works as a Standard Trie.
64
70
  Starting from version 0.1.0, you can obtain a Compressed Trie from the Standard one, by using the compression feature.
65
- Just call the `compress!` method on the `Rambling::Trie` instance:
71
+ Just call the `compress!` method on the trie instance:
66
72
 
67
73
  ``` ruby
68
74
  trie.compress!
@@ -72,10 +78,10 @@ This will reduce the amount of Trie nodes by eliminating the redundant ones, whi
72
78
 
73
79
  Starting from version 0.3.2, the `has_branch_for?` and `is_word?` methods work as expected on a compressed trie.
74
80
 
75
- __Note that the `compress!` method acts over the `Rambling::Trie` instance it belongs to.__
81
+ __Note that the `compress!` method acts over the trie instance it belongs to.__
76
82
  __Also, adding words after compression is not supported.__
77
83
 
78
- You can find out if a `Rambling::Trie` instance is compressed by calling the `compressed?` method:
84
+ You can find out if a trie instance is compressed by calling the `compressed?` method:
79
85
 
80
86
  ``` ruby
81
87
  trie.compressed?
data/lib/rambling-trie.rb CHANGED
@@ -1,22 +1,27 @@
1
1
  [
2
- 'rambling',
3
- 'rambling-trie/invalid_operation',
4
- 'rambling-trie/children_hash_deferer',
5
- 'rambling-trie/compressor',
6
- 'rambling-trie/branches',
7
- 'rambling-trie/node',
8
- 'rambling-trie/root',
9
- 'rambling-trie/version'
10
- ].each do |file|
11
- require File.join File.dirname(__FILE__), file
12
- end
2
+ 'invalid_operation',
3
+ 'children_hash_deferer',
4
+ 'compressor',
5
+ 'branches',
6
+ 'node',
7
+ 'root',
8
+ 'version'
9
+ ].map { |file| File.join('rambling-trie', file) }.each &method(:require)
13
10
 
14
11
  module Rambling
15
12
  module Trie
16
13
  class << self
14
+ # Creates a new Trie. Entry point for the Rambling::Trie API.
15
+ # @param [String, nil] filename the file to load the words from (defaults to nil).
17
16
  def create(*params)
18
17
  Root.new *params
19
18
  end
19
+
20
+ # @deprecated Please use {#create} instead
21
+ def new(*params)
22
+ warn '[DEPRECATION] `new` is deprecated. Please use `create` instead.'
23
+ create *params
24
+ end
20
25
  end
21
26
  end
22
27
  end
@@ -9,71 +9,65 @@ module Rambling
9
9
  def add_branch_from(word)
10
10
  raise InvalidOperation.new('Cannot add branch to compressed trie') if compressed?
11
11
  if word.empty?
12
- @is_terminal = true
12
+ @terminal = true
13
13
  return
14
14
  end
15
15
 
16
16
  first_letter = word.slice(0).to_sym
17
17
 
18
- if @children.has_key?(first_letter)
19
- word.slice!(0)
18
+ if @children.has_key? first_letter
19
+ word.slice! 0
20
20
  child = @children[first_letter]
21
- child.add_branch_from(word)
21
+ child << word
22
22
  child
23
23
  else
24
24
  @children[first_letter] = Node.new word, self
25
25
  end
26
26
  end
27
27
 
28
+ alias_method :<<, :add_branch_from
29
+
28
30
  protected
29
31
 
30
- def has_uncompressed_branch_for?(chars)
31
- chars.empty? or fulfills_uncompressed_condition?(:has_uncompressed_branch_for?, chars)
32
+ def uncompressed_has_branch_for?(chars)
33
+ chars.empty? or fulfills_uncompressed_condition?(:uncompressed_has_branch_for?, chars)
32
34
  end
33
35
 
34
- def has_compressed_branch_for?(chars)
36
+ def compressed_has_branch_for?(chars)
35
37
  return true if chars.empty?
36
38
 
37
- first_letter = chars.slice!(0)
38
- key = nil
39
- @children.keys.each do |x|
40
- x = x.to_s
41
- if x.start_with?(first_letter)
42
- key = x
43
- break
44
- end
45
- end
39
+ first_letter = chars.slice! 0
40
+ current_key, current_key_string = current_key first_letter
46
41
 
47
- unless key.nil?
48
- sym_key = key.to_sym
49
- return @children[sym_key].has_compressed_branch_for?(chars) if key.length == first_letter.length
42
+ unless current_key.nil?
43
+ return @children[current_key].compressed_has_branch_for?(chars) if current_key_string.length == first_letter.length
50
44
 
51
45
  while not chars.empty?
52
- char = chars.slice!(0)
46
+ char = chars.slice! 0
53
47
 
54
- break unless key[first_letter.length] == char
48
+ break unless current_key_string[first_letter.length] == char
55
49
 
56
50
  first_letter += char
57
51
  return true if chars.empty?
58
- return @children[sym_key].has_compressed_branch_for?(chars) if key.length == first_letter.length
52
+ return @children[current_key].compressed_has_branch_for?(chars) if current_key_string.length == first_letter.length
59
53
  end
60
54
  end
61
55
 
62
56
  false
63
57
  end
64
58
 
65
- def is_uncompressed_word?(chars)
66
- (chars.empty? and terminal?) or fulfills_uncompressed_condition?(:is_uncompressed_word?, chars)
59
+ def uncompressed_is_word?(chars)
60
+ (chars.empty? and terminal?) or fulfills_uncompressed_condition?(:uncompressed_is_word?, chars)
67
61
  end
68
62
 
69
- def is_compressed_word?(chars)
63
+ def compressed_is_word?(chars)
70
64
  return true if chars.empty? and terminal?
71
65
 
72
66
  first_letter = ''
73
67
  while not chars.empty?
74
- first_letter += chars.slice!(0)
68
+ first_letter += chars.slice! 0
75
69
  key = first_letter.to_sym
76
- return @children[key].is_compressed_word?(chars) if @children.has_key?(key)
70
+ return @children[key].compressed_is_word?(chars) if @children.has_key? key
77
71
  end
78
72
 
79
73
  false
@@ -81,11 +75,26 @@ module Rambling
81
75
 
82
76
  private
83
77
 
78
+ def current_key(letter)
79
+ current_key_string = current_key = nil
80
+
81
+ @children.keys.each do |key|
82
+ key_string = key.to_s
83
+ if key_string.start_with? letter
84
+ current_key = key
85
+ current_key_string = key_string
86
+ break
87
+ end
88
+ end
89
+
90
+ [current_key, current_key_string]
91
+ end
92
+
84
93
  def fulfills_uncompressed_condition?(method, chars)
85
- first_letter = chars.slice!(0)
94
+ first_letter = chars.slice! 0
86
95
  unless first_letter.nil?
87
96
  first_letter_sym = first_letter.to_sym
88
- return @children[first_letter_sym].send(method, chars) if @children.has_key?(first_letter_sym)
97
+ return @children[first_letter_sym].send(method, chars) if @children.has_key? first_letter_sym
89
98
  end
90
99
 
91
100
  false
@@ -1,34 +1,35 @@
1
1
  module Rambling
2
- # Provides some proxy methods to the children's hash for readability.
3
- module ChildrenHashDeferer
4
- # Proxies to @children[key]
5
- # @param [Symbol] key the key to look for in the children's hash.
6
- # @return [TrieNode, nil] the child node with that key or nil.
7
- def [](key)
8
- @children[key]
9
- end
2
+ module Trie
3
+ # Provides some proxy methods to the children's hash for readability.
4
+ module ChildrenHashDeferer
5
+ # Proxies to @children[key]
6
+ # @param [Symbol] key the key to look for in the children's hash.
7
+ # @return [Node, nil] the child node with that key or nil.
8
+ def [](key)
9
+ @children[key]
10
+ end
10
11
 
11
- # Proxies to @children[key] = value.
12
- # @param [Symbol] key the to add or change the value for.
13
- # @param [TrieNode] value the node to add to the children's hash.
14
- # @return [TrieNode, nil] the child node with that key or nil.
15
- def []=(key, value)
16
- @children[key] = value
17
- end
12
+ # Proxies to @children[key] = value.
13
+ # @param [Symbol] key the to add or change the value for.
14
+ # @param [Node] value the node to add to the children's hash.
15
+ # @return [Node, nil] the child node with that key or nil.
16
+ def []=(key, value)
17
+ @children[key] = value
18
+ end
18
19
 
19
- # Proxies to @children.delete(key)
20
- # @param [Symbol] key the key to delete in the children's hash.
21
- # @return [TrieNode, nil] the child node corresponding to the key just deleted or nil.
22
- def delete(key)
23
- @children.delete(key)
24
- end
20
+ # Proxies to @children.delete(key)
21
+ # @param [Symbol] key the key to delete in the children's hash.
22
+ # @return [Node, nil] the child node corresponding to the key just deleted or nil.
23
+ def delete(key)
24
+ @children.delete(key)
25
+ end
25
26
 
26
- # Proxies to @children.has_key?(key)
27
- # @param [Symbol] key the key to look for in the children's hash.
28
- # @return [Boolean] `true` for the keys that exist in the children's hash, false otherwise.
29
- def has_key?(key)
30
- @children.has_key?(key)
27
+ # Proxies to @children.has_key?(key)
28
+ # @param [Symbol] key the key to look for in the children's hash.
29
+ # @return [Boolean] `true` for the keys that exist in the children's hash, false otherwise.
30
+ def has_key?(key)
31
+ @children.has_key?(key)
32
+ end
31
33
  end
32
34
  end
33
35
  end
34
-
@@ -8,15 +8,13 @@ module Rambling
8
8
  @parent.nil? ? false : @parent.compressed?
9
9
  end
10
10
 
11
- protected
12
-
13
- def compress_own_tree!
11
+ def compress_tree!
14
12
  if @children.size == 1 and not terminal? and not @letter.nil?
15
- merge_with!(@children.values.first)
16
- compress_own_tree!
13
+ merge_with! @children.values.first
14
+ compress_tree!
17
15
  end
18
16
 
19
- @children.values.each { |node| node.compress_own_tree! }
17
+ @children.values.each &:compress_tree!
20
18
 
21
19
  self
22
20
  end
@@ -26,8 +24,8 @@ module Rambling
26
24
  def merge_with!(child)
27
25
  new_letter = (@letter.to_s + child.letter.to_s).to_sym
28
26
 
29
- rehash_on_parent!(@letter, new_letter)
30
- redefine_self!(new_letter, child)
27
+ rehash_on_parent! @letter, new_letter
28
+ redefine_self! new_letter, child
31
29
 
32
30
  @children.values.each { |node| node.parent = self }
33
31
  end
@@ -35,14 +33,14 @@ module Rambling
35
33
  def rehash_on_parent!(old_letter, new_letter)
36
34
  return if @parent.nil?
37
35
 
38
- @parent.delete(old_letter)
36
+ @parent.delete old_letter
39
37
  @parent[new_letter] = self
40
38
  end
41
39
 
42
40
  def redefine_self!(new_letter, merged_node)
43
41
  @letter = new_letter
44
42
  @children = merged_node.children
45
- @is_terminal = merged_node.terminal?
43
+ @terminal = merged_node.terminal?
46
44
  end
47
45
  end
48
46
  end
@@ -5,6 +5,7 @@ module Rambling
5
5
  include ChildrenHashDeferer
6
6
  include Compressor
7
7
  include Branches
8
+ include Enumerable
8
9
 
9
10
  # Letter or letters corresponding to this node.
10
11
  # @return [Symbol, nil] the corresponding letter(s) or nil.
@@ -15,37 +16,34 @@ module Rambling
15
16
  attr_reader :children
16
17
 
17
18
  # Parent node.
18
- # @return [TrieNode, nil] the parent node or nil for the root element.
19
+ # @return [Node, nil] the parent node or nil for the root element.
19
20
  attr_accessor :parent
20
21
 
21
- # Creates a new TrieNode.
22
- # @param [String] word the word from which to create this TrieNode and his branch.
23
- # @param [TrieNode] parent the parent of this node.
22
+ # Creates a new Node.
23
+ # @param [String] word the word from which to create this Node and his branch.
24
+ # @param [Node] parent the parent of this node.
24
25
  def initialize(word, parent = nil)
25
- @letter = nil
26
- @parent = parent
27
- @is_terminal = false
28
- @children = {}
26
+ @letter, @parent, @terminal, @children = [nil, parent, false, {}]
29
27
 
30
28
  unless word.nil? or word.empty?
31
- letter = word.slice!(0)
29
+ letter = word.slice! 0
32
30
  @letter = letter.to_sym unless letter.nil?
33
- @is_terminal = word.empty?
34
- add_branch_from(word)
31
+ @terminal = word.empty?
32
+ self << word
35
33
  end
36
34
  end
37
35
 
38
36
  # Flag for terminal nodes.
39
37
  # @return [Boolean] `true` for terminal nodes, `false` otherwise.
40
38
  def terminal?
41
- @is_terminal
39
+ @terminal
42
40
  end
43
41
 
44
42
  # String representation of the current node, if it is a terminal node.
45
43
  # @return [String] the string representation of the current node.
46
- # @raise [InvalidTrieOperation] if node is not terminal or is root.
44
+ # @raise [InvalidOperation] if node is not terminal or is root.
47
45
  def as_word
48
- raise InvalidOperation.new() unless @letter.nil? or terminal?
46
+ raise InvalidOperation.new unless @letter.nil? or terminal?
49
47
  get_letter_string
50
48
  end
51
49
 
@@ -8,50 +8,47 @@ module Rambling
8
8
  super(nil)
9
9
 
10
10
  @filename = filename
11
- @is_compressed = false
11
+ @compressed = false
12
12
  add_all_nodes if filename
13
13
  end
14
14
 
15
15
  # Compresses the existing tree using redundant node elimination. Flags the trie as compressed.
16
- # @return [Trie] same object
16
+ # @return [Root] same object
17
17
  def compress!
18
- unless compressed?
19
- compress_own_tree!
20
- @is_compressed = true
21
- end
22
-
18
+ @compressed = (not compress_tree!.nil?) unless compressed?
23
19
  self
24
20
  end
25
21
 
26
- # Flag for compressed tries. Overrides {TrieCompressor#compressed?}.
22
+ # Flag for compressed tries. Overrides {Compressor#compressed?}.
27
23
  # @return [Boolean] `true` for compressed tries, `false` otherwise.
28
24
  def compressed?
29
- @is_compressed = @is_compressed.nil? ? false : @is_compressed
25
+ @compressed
30
26
  end
31
27
 
32
28
  # Checks if a path for a word or partial word exists in the trie.
33
29
  # @param [String] word the word or partial word to look for in the trie.
34
30
  # @return [Boolean] `true` if the word or partial word is found, `false` otherwise.
35
31
  def has_branch_for?(word = '')
36
- chars = word.chars.to_a
37
- compressed? ? has_compressed_branch_for?(chars) : has_uncompressed_branch_for?(chars)
32
+ fulfills_condition word, :has_branch_for?
38
33
  end
39
34
 
40
35
  # Checks if a whole word exists in the trie.
41
36
  # @param [String] word the word to look for in the trie.
42
37
  # @return [Boolean] `true` only if the word is found and the last character corresponds to a terminal node.
43
38
  def is_word?(word = '')
44
- chars = word.chars.to_a
45
- compressed? ? is_compressed_word?(chars) : is_uncompressed_word?(chars)
39
+ fulfills_condition word, :is_word?
46
40
  end
47
41
 
42
+ alias_method :include?, :is_word?
43
+
48
44
  private
45
+ def fulfills_condition(word, method)
46
+ method = compressed? ? "compressed_#{method}" : "uncompressed_#{method}"
47
+ send(method, word.chars.to_a)
48
+ end
49
+
49
50
  def add_all_nodes
50
- File.open(@filename) do |file|
51
- while word = file.gets
52
- add_branch_from(word.chomp)
53
- end
54
- end
51
+ File.open(@filename).each_line { |line| self << line.chomp }
55
52
  end
56
53
  end
57
54
  end
@@ -9,8 +9,8 @@ namespace :performance do
9
9
  methods.each do |method|
10
10
  output.puts "`#{method}`:"
11
11
  words.each do |word|
12
- output.print "#{word} - #{trie.send(method, word)}".ljust(30)
13
- output.puts Benchmark.measure { 200_000.times {trie.send(method, word) }}
12
+ output.print "#{word} - #{trie.send(method, word)}".ljust 30
13
+ output.puts Benchmark.measure { 200_000.times { trie.send method, word }}
14
14
  end
15
15
  end
16
16
  end
@@ -18,21 +18,29 @@ namespace :performance do
18
18
  def generate_report(filename = nil)
19
19
  output = filename.nil? ? $stdout : File.open(filename, 'a+')
20
20
 
21
- trie = Rambling::Trie.new(get_path('assets', 'dictionaries', 'words_with_friends.txt'))
22
-
23
21
  output.puts "\nReport for rambling-trie version #{Rambling::Trie::VERSION}"
24
- report('Uncompressed', trie, output)
25
22
 
26
- return unless trie.respond_to?(:compress!)
23
+ trie = nil
24
+ measure = Benchmark.measure { trie = Rambling::Trie.create get_path('assets', 'dictionaries', 'words_with_friends.txt') }
25
+
26
+ if ENV['profile_creation']
27
+ output.puts '==> Creation'
28
+ output.print 'Rambling::Trie.create'.ljust 30
29
+ output.puts measure
30
+ end
31
+
32
+ report 'Uncompressed', trie, output
33
+
34
+ return unless trie.respond_to? :compress!
27
35
 
28
36
  trie.compress!
29
- report('Compressed', trie, output)
37
+ report 'Compressed', trie, output
30
38
 
31
39
  output.close
32
40
  end
33
41
 
34
42
  def get_path(*filename)
35
- File.join(File.dirname(__FILE__), '..', '..', '..', *filename)
43
+ File.join File.dirname(__FILE__), '..', '..', '..', *filename
36
44
  end
37
45
 
38
46
  desc 'Generate performance report'
@@ -45,7 +53,7 @@ namespace :performance do
45
53
  desc 'Generate performance report and append result to reports/performance'
46
54
  task :save do
47
55
  puts 'Generating performance report...'
48
- generate_report(get_path('reports', 'performance'))
56
+ generate_report get_path('reports', 'performance')
49
57
  puts 'Report has been saved to reports/performance'
50
58
  end
51
59
  end
@@ -56,7 +64,7 @@ namespace :performance do
56
64
 
57
65
  puts 'Generating profiling reports...'
58
66
 
59
- rambling_trie = Rambling::Trie.new(get_path('assets', 'dictionaries', 'words_with_friends.txt'))
67
+ rambling_trie = Rambling::Trie.create get_path('assets', 'dictionaries', 'words_with_friends.txt')
60
68
  words = ['hi', 'help', 'beautiful', 'impressionism', 'anthropological']
61
69
  methods = [:has_branch_for?, :is_word?]
62
70
  tries = [lambda {rambling_trie.clone}, lambda {rambling_trie.clone.compress!}]
@@ -66,12 +74,12 @@ namespace :performance do
66
74
  trie = trie_generator.call
67
75
  result = RubyProf.profile do
68
76
  words.each do |word|
69
- 200_000.times { trie.send(method, word) }
77
+ 200_000.times { trie.send method, word }
70
78
  end
71
79
  end
72
80
 
73
81
  File.open get_path('reports', "profile-#{trie.compressed? ? 'compressed' : 'uncompressed'}-#{method.to_s.sub(/\?/, '')}-#{Time.now.to_i}"), 'w' do |file|
74
- RubyProf::CallTreePrinter.new(result).print(file)
82
+ RubyProf::CallTreePrinter.new(result).print file
75
83
  end
76
84
  end
77
85
  end
@@ -85,7 +93,7 @@ namespace :performance do
85
93
 
86
94
  puts 'Generating cpu profiling reports...'
87
95
 
88
- rambling_trie = Rambling::Trie.new(get_path('assets', 'dictionaries', 'words_with_friends.txt'))
96
+ rambling_trie = Rambling::Trie.create get_path('assets', 'dictionaries', 'words_with_friends.txt')
89
97
  words = ['hi', 'help', 'beautiful', 'impressionism', 'anthropological']
90
98
  methods = [:has_branch_for?, :is_word?]
91
99
  tries = [lambda {rambling_trie.clone}, lambda {rambling_trie.clone.compress!}]
@@ -95,7 +103,7 @@ namespace :performance do
95
103
  trie = trie_generator.call
96
104
  result = PerfTools::CpuProfiler.start get_path('reports', "cpu_profile-#{trie.compressed? ? 'compressed' : 'uncompressed'}-#{method.to_s.sub(/\?/, '')}-#{Time.now.to_i}") do
97
105
  words.each do |word|
98
- 200_000.times { trie.send(method, word) }
106
+ 200_000.times { trie.send method, word }
99
107
  end
100
108
  end
101
109
  end
@@ -1,6 +1,6 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  # Current version of the rambling-trie.
4
- VERSION = '0.4.0'
4
+ VERSION = '0.4.1'
5
5
  end
6
6
  end
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
2
+ $:.push File.expand_path('../lib', __FILE__)
3
3
  require 'rambling-trie/version'
4
4
 
5
5
  Gem::Specification.new do |gem|
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ module Rambling
4
+ module Trie
5
+ describe Branches do
6
+ describe '#add_branch_from' do
7
+ context 'new word for existing branch' do
8
+ let(:node) { Node.new 'back' }
9
+
10
+ before :each do
11
+ node.add_branch_from 'a'
12
+ end
13
+
14
+ it 'does not increment the child count' do
15
+ node.should have(1).children
16
+ end
17
+
18
+ it 'marks it as terminal' do
19
+ node[:a].should be_terminal
20
+ end
21
+ end
22
+
23
+ context 'old word for existing branch' do
24
+ let(:node) { Node.new 'back' }
25
+
26
+ before :each do
27
+ node.add_branch_from 'ack'
28
+ end
29
+
30
+ it 'does not increment any child count' do
31
+ node.should have(1).children
32
+ node[:a].should have(1).children
33
+ node[:a][:c].should have(1).children
34
+ node[:a][:c][:k].should have(0).children
35
+ end
36
+ end
37
+ end
38
+
39
+ describe '#<<' do
40
+ let(:root) { Root.new }
41
+ let(:word) { 'word' }
42
+
43
+ it 'delegates to #add_branch_from' do
44
+ [true, false].each do |value|
45
+ root.stub(:add_branch_from).with(word).and_return value
46
+ root << word
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module Rambling
4
+ module Trie
5
+ describe ChildrenHashDeferer do
6
+ class ClassWithChildren
7
+ include ChildrenHashDeferer
8
+ attr_accessor :children
9
+
10
+ def initialize()
11
+ @children = {}
12
+ end
13
+ end
14
+
15
+ let(:deferer) { ClassWithChildren.new }
16
+
17
+ describe '#[]' do
18
+ let(:key) { :key }
19
+ let(:value) { 'value' }
20
+
21
+ it 'defers to the children hash' do
22
+ deferer.children.should_receive(:[]).with(key).and_return value
23
+ deferer[key].should == value
24
+ end
25
+ end
26
+
27
+ describe '#[]=' do
28
+ let(:key) { :key }
29
+ let(:value) { 'value' }
30
+
31
+ it 'defers to the children hash' do
32
+ deferer.children.should_receive(:[]=).with(key, value)
33
+ deferer[key] = value
34
+ end
35
+ end
36
+
37
+ describe '#delete' do
38
+ let(:key) { :key }
39
+ let(:value) { 'value' }
40
+
41
+ it 'defers to the children hash' do
42
+ deferer.children.should_receive(:delete).with(key).and_return value
43
+ deferer.delete(key).should == value
44
+ end
45
+ end
46
+
47
+ describe '#has_key' do
48
+ let(:key) { :key }
49
+
50
+ it 'defers to the children hash' do
51
+ [true, false].each do |value|
52
+ deferer.children.should_receive(:has_key?).with(key).and_return value
53
+ deferer.has_key?(key).should send("be_#{value}")
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -95,39 +95,6 @@ module Rambling
95
95
  end
96
96
  end
97
97
 
98
- describe '#add_branch_from' do
99
- context 'new word for existing branch' do
100
- let(:node) { Node.new 'back' }
101
-
102
- before :each do
103
- node.add_branch_from 'a'
104
- end
105
-
106
- it 'does not increment the child count' do
107
- node.should have(1).children
108
- end
109
-
110
- it 'marks it as terminal' do
111
- node[:a].should be_terminal
112
- end
113
- end
114
-
115
- context 'old word for existing branch' do
116
- let(:node) { Node.new 'back' }
117
-
118
- before :each do
119
- node.add_branch_from 'ack'
120
- end
121
-
122
- it 'does not increment any child count' do
123
- node.should have(1).children
124
- node[:a].should have(1).children
125
- node[:a][:c].should have(1).children
126
- node[:a][:c][:k].should have(0).children
127
- end
128
- end
129
- end
130
-
131
98
  describe '#as_word' do
132
99
  context 'for an empty node' do
133
100
  let(:node) { Node.new '' }
@@ -174,7 +141,7 @@ module Rambling
174
141
  end
175
142
 
176
143
  describe '#compressed?' do
177
- let(:root) { double('Root') }
144
+ let(:root) { double 'Root' }
178
145
  let(:node) { Node.new '', root }
179
146
 
180
147
  context 'parent is compressed' do
@@ -37,7 +37,7 @@ module Rambling
37
37
  end
38
38
 
39
39
  it 'loads every word' do
40
- File.open(filename) do |file|
40
+ File.open filename do |file|
41
41
  file.readlines.each { |word| root.is_word?(word.chomp).should be_true }
42
42
  end
43
43
  end
@@ -86,7 +86,7 @@ module Rambling
86
86
 
87
87
  context 'with at least one word' do
88
88
  it 'keeps the root letter nil' do
89
- root.add_branch_from('all')
89
+ root.add_branch_from 'all'
90
90
  root.compress!
91
91
 
92
92
  root.letter.should be_nil
@@ -95,7 +95,7 @@ module Rambling
95
95
 
96
96
  context 'with a single word' do
97
97
  before :each do
98
- root.add_branch_from('all')
98
+ root.add_branch_from 'all'
99
99
  root.compress!
100
100
  end
101
101
 
@@ -109,8 +109,8 @@ module Rambling
109
109
 
110
110
  context 'with two words' do
111
111
  before :each do
112
- root.add_branch_from('all')
113
- root.add_branch_from('ask')
112
+ root.add_branch_from 'all'
113
+ root.add_branch_from 'ask'
114
114
  root.compress!
115
115
  end
116
116
 
@@ -133,9 +133,9 @@ module Rambling
133
133
  end
134
134
 
135
135
  it 'reassigns the parent nodes correctly' do
136
- root.add_branch_from('repay')
137
- root.add_branch_from('rest')
138
- root.add_branch_from('repaint')
136
+ root.add_branch_from 'repay'
137
+ root.add_branch_from 'rest'
138
+ root.add_branch_from 'repaint'
139
139
  root.compress!
140
140
 
141
141
  root[:re].letter.should == :re
@@ -158,9 +158,9 @@ module Rambling
158
158
  end
159
159
 
160
160
  it 'does not compress terminal nodes' do
161
- root.add_branch_from('you')
162
- root.add_branch_from('your')
163
- root.add_branch_from('yours')
161
+ root.add_branch_from 'you'
162
+ root.add_branch_from 'your'
163
+ root.add_branch_from 'yours'
164
164
 
165
165
  root.compress!
166
166
 
@@ -175,9 +175,9 @@ module Rambling
175
175
 
176
176
  describe 'and trying to add a branch' do
177
177
  it 'raises an error' do
178
- root.add_branch_from('repay')
179
- root.add_branch_from('rest')
180
- root.add_branch_from('repaint')
178
+ root.add_branch_from 'repay'
179
+ root.add_branch_from 'rest'
180
+ root.add_branch_from 'repaint'
181
181
  root.compress!
182
182
 
183
183
  lambda { root.add_branch_from('restaurant') }.should raise_error(InvalidOperation)
@@ -191,8 +191,8 @@ module Rambling
191
191
  context 'word is contained' do
192
192
  shared_examples_for 'word is found' do
193
193
  it 'matches part of the word' do
194
- root.should have_branch_for('hell')
195
- root.should have_branch_for('hig')
194
+ root.should have_branch_for 'hell'
195
+ root.should have_branch_for 'hig'
196
196
  end
197
197
 
198
198
  it 'matches the whole word' do
@@ -220,8 +220,8 @@ module Rambling
220
220
  context 'word is not contained' do
221
221
  shared_examples_for 'word not found' do
222
222
  it 'does not match any part of the word' do
223
- root.should_not have_branch_for('ha')
224
- root.should_not have_branch_for('hal')
223
+ root.should_not have_branch_for 'ha'
224
+ root.should_not have_branch_for 'hal'
225
225
  end
226
226
 
227
227
  it 'does not match the whole word' do
@@ -244,6 +244,18 @@ module Rambling
244
244
  end
245
245
  end
246
246
  end
247
+
248
+ describe '#include?' do
249
+ let(:root) { Root.new }
250
+ let(:word) { 'word' }
251
+
252
+ it 'delegates to #is_word?' do
253
+ [true, false].each do |value|
254
+ root.stub(:is_word?).with(word).and_return value
255
+ root.include?(word).should &method("be_#{value}".to_sym)
256
+ end
257
+ end
258
+ end
247
259
  end
248
260
  end
249
261
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  module Rambling
4
4
  describe Trie do
5
5
  describe '.create' do
6
- let(:root) { double('Trie::Root') }
6
+ let(:root) { double 'Trie::Root' }
7
7
 
8
8
  before :each do
9
9
  Trie::Root.stub(:new).and_return root
@@ -13,5 +13,22 @@ module Rambling
13
13
  Trie.create.should == root
14
14
  end
15
15
  end
16
+
17
+ describe '.new' do
18
+ let(:root) { double 'Trie::Root' }
19
+
20
+ before :each do
21
+ Trie.should_receive(:create).and_return root
22
+ end
23
+
24
+ it 'returns the new trie root node instance' do
25
+ Trie.new.should == root
26
+ end
27
+
28
+ it 'warns about deprecation' do
29
+ Trie.should_receive(:warn).with '[DEPRECATION] `new` is deprecated. Please use `create` instead.'
30
+ Trie.new
31
+ end
32
+ end
16
33
  end
17
34
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rambling-trie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-16 00:00:00.000000000 Z
12
+ date: 2012-07-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -118,10 +118,11 @@ files:
118
118
  - lib/rambling-trie/tasks/gem.rb
119
119
  - lib/rambling-trie/tasks/performance.rb
120
120
  - lib/rambling-trie/version.rb
121
- - lib/rambling.rb
122
121
  - rambling-trie.gemspec
123
122
  - reports/performance
124
123
  - spec/assets/test_words.txt
124
+ - spec/lib/rambling-trie/branches_spec.rb
125
+ - spec/lib/rambling-trie/children_hash_deferer_spec.rb
125
126
  - spec/lib/rambling-trie/node_spec.rb
126
127
  - spec/lib/rambling-trie/root_spec.rb
127
128
  - spec/lib/rambling-trie_spec.rb
@@ -140,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
140
141
  version: '0'
141
142
  segments:
142
143
  - 0
143
- hash: 4431661541805612583
144
+ hash: 1932693806363009255
144
145
  required_rubygems_version: !ruby/object:Gem::Requirement
145
146
  none: false
146
147
  requirements:
@@ -149,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
150
  version: '0'
150
151
  segments:
151
152
  - 0
152
- hash: 4431661541805612583
153
+ hash: 1932693806363009255
153
154
  requirements: []
154
155
  rubyforge_project:
155
156
  rubygems_version: 1.8.24
data/lib/rambling.rb DELETED
@@ -1,3 +0,0 @@
1
- # Entry point for the rambling-trie. General Namespace.
2
- module Rambling
3
- end