rambling-trie 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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