rambling-trie 0.9.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/LICENSE +1 -1
  4. data/README.md +133 -26
  5. data/Rakefile +1 -2
  6. data/lib/rambling/trie.rb +53 -9
  7. data/lib/rambling/trie/comparable.rb +16 -0
  8. data/lib/rambling/trie/compressable.rb +14 -0
  9. data/lib/rambling/trie/compressed_node.rb +38 -14
  10. data/lib/rambling/trie/compressor.rb +14 -10
  11. data/lib/rambling/trie/configuration.rb +11 -0
  12. data/lib/rambling/trie/configuration/properties.rb +66 -0
  13. data/lib/rambling/trie/configuration/provider_collection.rb +101 -0
  14. data/lib/rambling/trie/container.rb +57 -17
  15. data/lib/rambling/trie/enumerable.rb +1 -1
  16. data/lib/rambling/trie/forwardable.rb +9 -4
  17. data/lib/rambling/trie/inspectable.rb +37 -0
  18. data/lib/rambling/trie/invalid_operation.rb +3 -2
  19. data/lib/rambling/trie/missing_node.rb +2 -1
  20. data/lib/rambling/trie/node.rb +40 -30
  21. data/lib/rambling/trie/raw_node.rb +29 -13
  22. data/lib/rambling/trie/readers.rb +11 -0
  23. data/lib/rambling/trie/readers/plain_text.rb +26 -0
  24. data/lib/rambling/trie/serializers.rb +11 -0
  25. data/lib/rambling/trie/serializers/file.rb +25 -0
  26. data/lib/rambling/trie/serializers/marshal.rb +38 -0
  27. data/lib/rambling/trie/serializers/yaml.rb +39 -0
  28. data/lib/rambling/trie/serializers/zip.rb +67 -0
  29. data/lib/rambling/trie/stringifyable.rb +20 -0
  30. data/lib/rambling/trie/version.rb +1 -1
  31. data/rambling-trie.gemspec +2 -2
  32. data/spec/integration/rambling/trie_spec.rb +45 -49
  33. data/spec/lib/rambling/trie/comparable_spec.rb +104 -0
  34. data/spec/lib/rambling/trie/compressed_node_spec.rb +44 -0
  35. data/spec/lib/rambling/trie/configuration/properties_spec.rb +49 -0
  36. data/spec/lib/rambling/trie/configuration/provider_collection_spec.rb +165 -0
  37. data/spec/lib/rambling/trie/container_spec.rb +127 -38
  38. data/spec/lib/rambling/trie/{inspector_spec.rb → inspectable_spec.rb} +7 -5
  39. data/spec/lib/rambling/trie/raw_node_spec.rb +22 -41
  40. data/spec/lib/rambling/trie/readers/plain_text_spec.rb +14 -0
  41. data/spec/lib/rambling/trie/serializers/file_spec.rb +11 -0
  42. data/spec/lib/rambling/trie/serializers/marshal_spec.rb +14 -0
  43. data/spec/lib/rambling/trie/serializers/yaml_spec.rb +14 -0
  44. data/spec/lib/rambling/trie/serializers/zip_spec.rb +30 -0
  45. data/spec/lib/rambling/trie/stringifyable_spec.rb +82 -0
  46. data/spec/lib/rambling/trie_spec.rb +120 -7
  47. data/spec/spec_helper.rb +7 -1
  48. data/spec/support/config.rb +5 -0
  49. data/spec/support/shared_examples/a_compressable_trie.rb +26 -0
  50. data/spec/support/shared_examples/a_serializable_trie.rb +26 -0
  51. data/spec/support/shared_examples/a_serializer.rb +29 -0
  52. data/spec/support/shared_examples/a_trie_data_structure.rb +29 -0
  53. data/spec/tmp/.gitkeep +0 -0
  54. metadata +51 -24
  55. data/lib/rambling/trie/compression.rb +0 -13
  56. data/lib/rambling/trie/inspector.rb +0 -11
  57. data/lib/rambling/trie/plain_text_reader.rb +0 -23
  58. data/lib/rambling/trie/tasks/gem.rb +0 -17
  59. data/lib/rambling/trie/tasks/helpers/path.rb +0 -17
  60. data/lib/rambling/trie/tasks/helpers/performance_report.rb +0 -17
  61. data/lib/rambling/trie/tasks/helpers/time.rb +0 -7
  62. data/lib/rambling/trie/tasks/performance.rb +0 -15
  63. data/lib/rambling/trie/tasks/performance/all.rb +0 -17
  64. data/lib/rambling/trie/tasks/performance/benchmark.rb +0 -201
  65. data/lib/rambling/trie/tasks/performance/directory.rb +0 -11
  66. data/lib/rambling/trie/tasks/performance/flamegraph.rb +0 -119
  67. data/lib/rambling/trie/tasks/performance/profile/call_tree.rb +0 -147
  68. data/lib/rambling/trie/tasks/performance/profile/memory.rb +0 -143
  69. data/spec/lib/rambling/trie/plain_text_reader_spec.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 54be3d6a000a38c0864250d0951a1986b2ceac2c
4
- data.tar.gz: 5a8b13f581ce79c239f71e7e96c014572697f0c6
3
+ metadata.gz: 5e58cb5a9636b79abd8fb93b4fae30ec3486871a
4
+ data.tar.gz: 5cb485068db0e4a889692433eedb1aa1682394f1
5
5
  SHA512:
6
- metadata.gz: 62bf472c3abfc664ca22e2df3ba55254d3e03e1d66ab7f3f64bba4755feaf6e4ce92de604ff5c7b479056840734b4e3ca0be4707af447f0521c327d1bbc274e6
7
- data.tar.gz: c13d4ce29e89093292268426240cc543026f11a4e0747a33c65bcd7950b156629a39ac61a9a5a78a95320de82d734680a532a043ac78a4c56e0334e746f9bb72
6
+ metadata.gz: 7d035ffc3fa86ee5be924cac9f73701df016221158e5654d3c908ec2adf034f76bf2d9081d56027c5e22c812a99241f671de64826f6b094fba153c62014edaeb
7
+ data.tar.gz: ac2151ec179554ed23d990e92fdfbce683ca88086f04eaa4becf4ad392c77a947327766ea59c4de561e62d741ccd815d637241f415e16fcebbebb2d712a4cf0a
data/Gemfile CHANGED
@@ -3,6 +3,8 @@ source 'https://rubygems.org'
3
3
 
4
4
  gemspec
5
5
 
6
+ gem 'rubyzip'
7
+
6
8
  group :development do
7
9
  gem 'ruby-prof'
8
10
  gem 'memory_profiler'
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-2016 Edgar Gonzalez
1
+ Copyright (c) 2012-2017 Edgar Gonzalez
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Gem Version][badge_fury_badge]][badge_fury_link] [![Dependency Status][gemnasium_badge]][gemnasium_link] [![Build Status][travis_ci_badge]][travis_ci_link] [![Code Climate][code_climate_badge]][code_climage_link] [![Coverage Status][coveralls_badge]][coveralls_link] [![Documentation Status][inch_ci_badge]][inch_ci_link]
4
4
 
5
- The Rambling Trie is a custom implementation of the Trie data structure with Ruby, which includes compression abilities and is designed to be very fast to traverse.
5
+ The Rambling Trie is a Ruby implementation of the [trie data structure][trie_wiki], which includes compression abilities and is designed to be very fast to traverse.
6
6
 
7
7
  ## Installing the Rambling Trie
8
8
 
@@ -13,7 +13,7 @@ You will need:
13
13
  * Ruby 2.1.0 or up
14
14
  * RubyGems
15
15
 
16
- See [RVM][rvm] or [rbenv][rbenv] for more information on how to manage Ruby versions.
16
+ See [RVM][rvm], [rbenv][rbenv] or [chruby][chruby] for more information on how to manage Ruby versions.
17
17
 
18
18
  ### Installation
19
19
 
@@ -29,15 +29,17 @@ Or, include it in your `Gemfile` and bundle it:
29
29
  gem 'rambling-trie'
30
30
  ```
31
31
 
32
- ## How to use the Rambling Trie
32
+ ## Using the Rambling Trie
33
33
 
34
- To create the trie, initialize it like this:
34
+ ### Creation
35
+
36
+ To create a new trie, initialize it like this:
35
37
 
36
38
  ``` ruby
37
39
  trie = Rambling::Trie.create
38
40
  ```
39
41
 
40
- You can also provide a block and the created instance will be yielded for you to perform any operation on it:
42
+ You can also provide a block and the created trie instance will be yielded for you to perform any operation on it:
41
43
 
42
44
  ``` ruby
43
45
  Rambling::Trie.create do |trie|
@@ -62,52 +64,67 @@ the
62
64
  trie
63
65
  ```
64
66
 
65
- If you want to use a custom file format, you will need to provide a custom file reader that defines the `each_word` method that yields each word contained in the file. Look at the `Rambling::Trie::PlainTextReader` class for an example.
67
+ If you want to use a custom file format, you will need to provide a custom file reader that defines an `#each_word` method that yields each word contained in the file. Look at the [`PlainText` reader][rambling_trie_plain_text_reader] class for an example, and at the [Configuration section][rambling_trie_configuration] to see how to add your own custom file readers.
68
+
69
+ ### Operations
66
70
 
67
- To add new words to the trie, use `add` or `<<`:
71
+ To add new words to the trie, use `#add` or its alias `#<<`:
68
72
 
69
73
  ``` ruby
70
74
  trie.add 'word'
71
75
  trie << 'word'
72
76
  ```
73
77
 
74
- And to find out if a word already exists in the trie, use `word?` or `include?`:
78
+ And to find out if a word already exists in the trie, use `#word?` or its alias `#include?`:
75
79
 
76
80
  ``` ruby
77
81
  trie.word? 'word'
78
82
  trie.include? 'word'
79
83
  ```
80
84
 
81
- If you wish to find if part of a word exists in the trie instance, you should call `partial_word?`:
85
+ If you wish to find if part of a word exists in the trie instance, you should call `#partial_word?` or its alias `#match?`:
82
86
 
83
87
  ``` ruby
84
88
  trie.partial_word? 'partial_word'
85
89
  trie.match? 'partial_word'
86
90
  ```
87
91
 
88
- To get all the words that start with a particular string, you can use `scan`:
92
+ To get all the words that start with a particular string, you can use `#scan` or its alias `#words`:
89
93
 
90
94
  ``` ruby
91
95
  trie.scan 'hi' # => ['hi', 'high', 'highlight', ...]
92
96
  trie.words 'hi' # => ['hi', 'high', 'highlight', ...]
93
97
  ```
94
98
 
99
+ To get all the words within a given string, you can use `#words_within`:
100
+
101
+ ``` ruby
102
+ trie.words_within 'ifdxawesome45someword3' # => ['if', 'aw', 'awe', ...]
103
+ trie.words_within 'tktktktk' # => []
104
+ ```
105
+
106
+ Or, if you're just interested in knowing whether a given string contains any valid words or not, you can use `#words_within?`:
107
+
108
+ ``` ruby
109
+ trie.words_within? 'ifdxawesome45someword3' # => true
110
+ trie.words_within? 'tktktktk' # => false
111
+ ```
112
+
95
113
  ### Compression
96
114
 
97
- By default, the Rambling Trie works as a Standard Trie.
98
- Starting from version 0.1.0, you can obtain a Compressed Trie from the Standard one, by using the compression feature.
99
- Just call the `compress!` method on the trie instance:
115
+ By default, the Rambling Trie works as a standard trie. Starting from version 0.1.0, you can obtain a compressed trie from the standard one, by using the compression feature. Just call the `#compress!` method on the trie instance:
100
116
 
101
117
  ``` ruby
102
118
  trie.compress!
103
119
  ```
104
120
 
105
- This will reduce the amount of Trie nodes by eliminating the redundant ones, which are the only-child non-terminal nodes.
121
+ This will reduce the size of the trie by using redundant node elimination (redundant nodes are the only-child non-terminal nodes).
106
122
 
107
- __Note that the `compress!` method acts over the trie instance it belongs to.__
108
- __Also, adding words after compression is not supported.__
123
+ > _**Note**: The `#compress!` method acts over the trie instance it belongs to
124
+ > and is destructive. Also, adding words after compression (with `#add` or
125
+ > `#<<`) is not supported._
109
126
 
110
- You can find out if a trie instance is compressed by calling the `compressed?` method:
127
+ You can find out if a trie instance is compressed by calling the `#compressed?` method:
111
128
 
112
129
  ``` ruby
113
130
  trie.compressed?
@@ -118,15 +135,93 @@ trie.compressed?
118
135
  Starting from version 0.4.2, you can use any `Enumerable` method over a trie instance, and it will iterate over each word contained in the trie. You can now do things like:
119
136
 
120
137
  ``` ruby
121
- trie.each do |word|
122
- puts word
123
- end
124
-
138
+ trie.each { |word| puts word }
125
139
  trie.any? { |word| word.include? 'x' }
140
+ trie.all? { |word| word.include? 'x' }
126
141
  # etc.
127
142
  ```
128
143
 
129
- ## Further Documentation
144
+ ### Serialization
145
+
146
+ Starting from version 1.0.0, you can store a full trie instance on disk and retrieve/use it later on. Loading a trie from disk takes less time, less cpu and less memory than loading every word into the trie every time. This is particularly useful for production applications, when you have word lists that you know are going to be static, or that change with little frequency.
147
+
148
+ To store a trie on disk, you can use `.dump` like this:
149
+
150
+ ``` ruby
151
+ Rambling::Trie.dump trie, '/path/to/file'
152
+ ```
153
+
154
+ Then, when you need to use a trie next time, you don't have to create a new one with all the necessary words. Rather, you can retrieve a previously stored one with `.load` like this:
155
+
156
+ ``` ruby
157
+ trie = Rambling::Trie.load trie, '/path/to/file'
158
+ ```
159
+
160
+ #### Supported formats
161
+
162
+ Currently, these formats are supported to store tries on disk:
163
+
164
+ - Ruby's [binary (Marshal)][marshal] format
165
+ - [YAML][yaml]
166
+
167
+ > When dumping into or loading from disk, the format is determined
168
+ > automatically based on the file extension, so `.yml` or `.yaml` files will be
169
+ > handled through `YAML` and `.marshal` files through `Marshal`.
170
+
171
+ Optionally, you can use a `.zip` version of the supported formats. In order to do so, you'll have to install the [`rubyzip`][rubyzip] gem:
172
+
173
+ ``` bash
174
+ gem install rubyzip
175
+ ```
176
+
177
+ Or, include it in your `Gemfile` and bundle it:
178
+
179
+ ``` ruby
180
+ gem 'rubyzip'
181
+ ```
182
+
183
+ Then, you can load contents form a `.zip` file like this:
184
+
185
+ ``` ruby
186
+ require 'zip'
187
+ trie = Rambling::Trie.load trie, '/path/to/file.zip'
188
+ ```
189
+
190
+ > For `.zip` files, the format is also determined automatically based on the
191
+ > file extension, so `.yml.zip` or `.yaml.zip` files will be handled through
192
+ > `YAML` after decompression and `.marshal.zip` files through `Marshal`.
193
+
194
+ ### Configuration
195
+
196
+ Starting from version 1.0.0, you can change the configuration values used by Rambling Trie. You can now supply:
197
+
198
+ * A `Compressor` object
199
+ * A root `Node` builder
200
+ * More `Readers` (implement `#each_word`)
201
+ * Change the default `reader`
202
+ * More `Serializers` (implement `#dump` and `#load`)
203
+ * Change the default `serializer`
204
+
205
+ You can configure those values by using `.config` like this:
206
+
207
+ ```ruby
208
+ require 'rambling-trie'
209
+
210
+ Rambling::Trie.config do |config|
211
+ config.compressor = MyCompressor.new
212
+ config.root_builder = lambda { MyNode.new }
213
+
214
+ config.readers.add :html, MyHtmlReader.new
215
+ config.readers.default = config.readers[:html]
216
+
217
+ config.serializers.add :json, MyJsonSerializer.new
218
+ config.serializers.default = config.serializers[:yml]
219
+ end
220
+
221
+ # Create a trie or load one from disk and do things with it...
222
+ ```
223
+
224
+ ### Further Documentation
130
225
 
131
226
  You can find further API documentation on the autogenerated [rambling-trie gem RubyDoc.info page][rubydoc] or if you want edge documentation, you can go the [GitHub project RubyDoc.info page][rubydoc_github].
132
227
 
@@ -139,16 +234,19 @@ The Rambling Trie has been tested with the following Ruby versions:
139
234
  * 2.2.x
140
235
  * 2.1.x
141
236
 
142
- Ruby 1.8.x, 1.9.x and 2.0.x are not supported.
237
+ **No longer supported**:
238
+
239
+ * 2.0.x (might still work, but is not officially supported)
240
+ * 1.9.x
241
+ * 1.8.x
143
242
 
144
243
  ## Contributing to Rambling Trie
145
244
 
146
- If you want to contribute to this project, you will need RSpec to run the tests.
147
- Also, be sure to add tests for any feature you may develop or bug you may fix.
245
+ Take a look at the [contributing guide][rambling_trie_contributing_guide] to get started, or fire a question to [@gonzedge][github_user_gonzedge].
148
246
 
149
247
  ## License and copyright
150
248
 
151
- Copyright (c) 2012-2016 Edgar Gonzalez
249
+ Copyright (c) 2012-2017 Edgar Gonzalez
152
250
 
153
251
  MIT License
154
252
 
@@ -160,17 +258,26 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
160
258
 
161
259
  [badge_fury_badge]: https://badge.fury.io/rb/rambling-trie.svg
162
260
  [badge_fury_link]: https://badge.fury.io/rb/rambling-trie
261
+ [chruby]: https://github.com/postmodern/chruby
163
262
  [code_climage_link]: https://codeclimate.com/github/gonzedge/rambling-trie
164
263
  [code_climate_badge]: https://codeclimate.com/github/gonzedge/rambling-trie/badges/gpa.svg
165
264
  [coveralls_badge]: https://img.shields.io/coveralls/gonzedge/rambling-trie.svg
166
265
  [coveralls_link]: https://coveralls.io/r/gonzedge/rambling-trie
167
266
  [gemnasium_badge]: https://gemnasium.com/gonzedge/rambling-trie.svg
168
267
  [gemnasium_link]: https://gemnasium.com/gonzedge/rambling-trie
268
+ [github_user_gonzedge]: https://github.com/gonzedge
169
269
  [inch_ci_badge]: https://inch-ci.org/github/gonzedge/rambling-trie.svg?branch=master
170
270
  [inch_ci_link]: https://inch-ci.org/github/gonzedge/rambling-trie
271
+ [marshal]: https://ruby-doc.org/core-2.4.0/Marshal.html
272
+ [rambling_trie_configuration]: https://github.com/gonzedge/rambling-trie#configuration
273
+ [rambling_trie_contributing_guide]: https://github.com/gonzedge/rambling-trie/blob/master/CONTRIBUTING.md
274
+ [rambling_trie_plain_text_reader]: https://github.com/gonzedge/rambling-trie/blob/master/lib/rambling/trie/readers/plain_text.rb
171
275
  [rbenv]: https://github.com/sstephenson/rbenv
172
276
  [rubydoc]: http://rubydoc.info/gems/rambling-trie
173
277
  [rubydoc_github]: http://rubydoc.info/github/gonzedge/rambling-trie
278
+ [rubyzip]: https://github.com/rubyzip/rubyzip
174
279
  [rvm]: https://rvm.io
175
280
  [travis_ci_badge]: https://travis-ci.org/gonzedge/rambling-trie.svg
176
281
  [travis_ci_link]: https://travis-ci.org/gonzedge/rambling-trie
282
+ [trie_wiki]: https://en.wikipedia.org/wiki/Trie
283
+ [yaml]: https://ruby-doc.org/stdlib-2.4.0/libdoc/yaml/rdoc/YAML.html
data/Rakefile CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
- require 'rambling-trie'
4
- require 'rambling/trie/tasks/performance'
3
+ require_relative 'tasks/performance'
5
4
 
6
5
  RSpec::Core::RakeTask.new :spec
7
6
 
@@ -1,8 +1,9 @@
1
1
  require 'forwardable'
2
+
2
3
  %w{
3
- forwardable compression compressor inspector container enumerable
4
- invalid_operation plain_text_reader node missing_node compressed_node
5
- raw_node version
4
+ forwardable comparable compressable compressor configuration container
5
+ enumerable inspectable invalid_operation readers serializers stringifyable
6
+ node missing_node compressed_node raw_node version
6
7
  }.each do |file|
7
8
  require File.join('rambling', 'trie', file)
8
9
  end
@@ -12,15 +13,25 @@ module Rambling
12
13
  # Entry point for rambling-trie API.
13
14
  module Trie
14
15
  class << self
15
- # Creates a new Trie. Entry point for the Rambling::Trie API.
16
+ extend Rambling::Trie::Forwardable
17
+
18
+ delegate [
19
+ :readers,
20
+ :serializers,
21
+ :compressor,
22
+ :root_builder
23
+ ] => :properties
24
+
25
+ # Creates a new Rambling::Trie. Entry point for the Rambling::Trie API.
16
26
  # @param [String, nil] filepath the file to load the words from.
27
+ # @param [Reader, nil] reader the file parser to get each word. See
28
+ # {Rambling::Trie::Readers Readers}.
17
29
  # @return [Container] the trie just created.
18
30
  # @yield [Container] the trie just created.
19
31
  def create filepath = nil, reader = nil
20
- reader ||= default_reader
21
-
22
- Rambling::Trie::Container.new do |container|
32
+ Rambling::Trie::Container.new root_builder.call, compressor do |container|
23
33
  if filepath
34
+ reader ||= readers.resolve filepath
24
35
  reader.each_word filepath do |word|
25
36
  container << word
26
37
  end
@@ -30,10 +41,43 @@ module Rambling
30
41
  end
31
42
  end
32
43
 
44
+ # Loads an existing trie from disk into memory.
45
+ # @param [String] filepath the file to load the words from.
46
+ # @param [Serializer, nil] serializer the object responsible of loading the trie
47
+ # from disk. See {Rambling::Trie::Serializers Serializers}.
48
+ # @return [Container] the trie just loaded.
49
+ # @yield [Container] the trie just loaded.
50
+ def load filepath, serializer = nil
51
+ serializer ||= serializers.resolve filepath
52
+ root = serializer.load filepath
53
+ Rambling::Trie::Container.new root, compressor do |container|
54
+ yield container if block_given?
55
+ end
56
+ end
57
+
58
+ # Dumps an existing trie from memory into disk.
59
+ # @param [Container] trie the trie to dump into disk.
60
+ # @param [String] filepath the file to dump to serialized trie into.
61
+ # @param [Serializer, nil] serializer the object responsible of
62
+ # serializing and dumping the trie into disk. See
63
+ # {Rambling::Trie::Serializers Serializers}.
64
+ def dump trie, filepath, serializer = nil
65
+ serializer ||= serializers.resolve filepath
66
+ serializer.dump trie.root, filepath
67
+ end
68
+
69
+ # Provides configuration properties for the Rambling::Trie gem.
70
+ # @return [Properties] the configured properties of the gem.
71
+ # @yield [Properties] the configured properties of the gem.
72
+ def config
73
+ yield properties if block_given?
74
+ properties
75
+ end
76
+
33
77
  private
34
78
 
35
- def default_reader
36
- Rambling::Trie::PlainTextReader.new
79
+ def properties
80
+ @properties ||= Rambling::Trie::Configuration::Properties.new
37
81
  end
38
82
  end
39
83
  end
@@ -0,0 +1,16 @@
1
+ module Rambling
2
+ module Trie
3
+ # Provides the comparable behavior for the trie data structure.
4
+ module Comparable
5
+ # Compares two nodes.
6
+ # @param [Node] other the node to compare against.
7
+ # @return [Boolean] `true` if the nodes' {Node#letter #letter} and
8
+ # {Node#children_tree #children_tree} are equal, `false` otherwise.
9
+ def == other
10
+ letter == other.letter &&
11
+ terminal? == other.terminal? &&
12
+ children_tree == other.children_tree
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module Rambling
2
+ module Trie
3
+ # Provides the compressable behavior for the trie data structure.
4
+ module Compressable
5
+ # Indicates if the current {Rambling::Trie::Node Node} can be compressed
6
+ # or not.
7
+ # @return [Boolean] `true` for non-{Node#terminal? terminal} nodes with
8
+ # one child, `false` otherwise.
9
+ def compressable?
10
+ !(root? || terminal?) && children_tree.size == 1
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,39 +1,41 @@
1
1
  module Rambling
2
2
  module Trie
3
- # A representation of a node in an compressed Trie data structure.
3
+ # A representation of a node in an compressed trie data structure.
4
4
  class CompressedNode < Rambling::Trie::Node
5
- # Always raises [Rambling::Trie::InvalidOperation] when trying to add a
6
- # branch to the current trie node based on the word
7
- # @param [String] word the word to add the branch from.
5
+ # Always raises {Rambling::Trie::InvalidOperation InvalidOperation} when
6
+ # trying to add a word to the current compressed trie node
7
+ # @param [String] word the word to add to the trie.
8
8
  # @raise [InvalidOperation] if the trie is already compressed.
9
+ # @return [nil] this never returns as it always raises an exception.
9
10
  def add word
10
- raise Rambling::Trie::InvalidOperation, 'Cannot add branch to compressed trie'
11
+ raise Rambling::Trie::InvalidOperation, 'Cannot add word to compressed trie'
11
12
  end
12
13
 
13
- # Checks if a path for set of characters exists in the trie.
14
- # @param [Array] chars the characters to look for in the trie.
14
+ # Checks if a path for set a of characters exists in the trie.
15
+ # @param [Array<String>] chars the characters to look for in the trie.
15
16
  # @return [Boolean] `true` if the characters are found, `false` otherwise.
16
17
  def partial_word? chars
17
18
  chars.empty? || has_partial_word?(chars)
18
19
  end
19
20
 
20
21
  # Checks if a path for set of characters represents a word in the trie.
21
- # @param [Array] chars the characters to look for in the trie.
22
+ # @param [Array<String>] chars the characters to look for in the trie.
22
23
  # @return [Boolean] `true` if the characters are found and form a word,
23
- # `false` otherwise.
24
+ # `false` otherwise.
24
25
  def word? chars
25
26
  chars.empty? ? terminal? : has_word?(chars)
26
27
  end
27
28
 
28
- # Returns all words that start with the specified characters.
29
- # @param [Array] chars the characters to look for in the trie.
30
- # @return [Array] all the words contained in the trie that start with the specified characters.
29
+ # Returns the node that starts with the specified characters.
30
+ # @param [Array<String>] chars the characters to look for in the trie.
31
+ # @return [Node] the node that matches the specified characters.
32
+ # {MissingNode MissingNode} when not found.
31
33
  def scan chars
32
34
  chars.empty? ? self : closest_node(chars)
33
35
  end
34
36
 
35
- # Always return `true` for a raw (compressed) node.
36
- # @return [Boolean] always true for a raw (compressed) node.
37
+ # Always return `true` for a compressed node.
38
+ # @return [Boolean] always `true` for a compressed node.
37
39
  def compressed?
38
40
  true
39
41
  end
@@ -65,6 +67,28 @@ module Rambling
65
67
  recursive_get(:scan, chars) || Rambling::Trie::MissingNode.new
66
68
  end
67
69
 
70
+ def children_match_prefix chars
71
+ return enum_for :children_match_prefix, chars unless block_given?
72
+
73
+ current_key = nil
74
+
75
+ while !chars.empty?
76
+ if current_key
77
+ current_key << chars.slice!(0)
78
+ else
79
+ current_key = chars.slice!(0)
80
+ end
81
+
82
+ child = children_tree[current_key.to_sym]
83
+
84
+ if child
85
+ child.match_prefix chars do |word|
86
+ yield word
87
+ end
88
+ end
89
+ end
90
+ end
91
+
68
92
  def recursive_get method, chars
69
93
  current_length = 0
70
94
  current_key = current_key chars.slice!(0)