autocompletion 0.0.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.
@@ -0,0 +1,51 @@
1
+ README
2
+ ======
3
+
4
+
5
+ Summary
6
+ -------
7
+ This gem provides fast prefix-autocompletion in pure ruby.
8
+
9
+
10
+ Installation
11
+ ------------
12
+ `gem install autocompletion`
13
+
14
+
15
+ Usage
16
+ -----
17
+
18
+ ### Autocomplete words
19
+ auto = AutoCompletion.words(%w[foo bar baz])
20
+ auto.complete('f') # => ["foo"]
21
+ auto.complete('b') # => ["bar", "baz"]
22
+ auto.complete('z') # => []
23
+
24
+ ### Autocomplete objects by attributes
25
+ Person = Struct.new(:first_name, :last_name)
26
+ people = [
27
+ Person.new("Peter", "Parker"),
28
+ Person.new("Luke", "Skywalker"),
29
+ Person.new("Anakin", "Skywalker"),
30
+ ]
31
+ auto = AutoCompletion.map(people) { |person|
32
+ [person.first_name, person.last_name]
33
+ }
34
+
35
+ auto.complete("P")
36
+ # => [#<struct Person first_name="Peter", last_name="Parker">]
37
+
38
+ auto.complete("S")
39
+ # => [#<struct Person first_name="Luke", last_name="Skywalker">,
40
+ # #<struct Person first_name="Anakin", last_name="Skywalker">]
41
+
42
+ auto.complete("S", "L")
43
+ # => [#<struct Person first_name="Luke", last_name="Skywalker">]
44
+
45
+
46
+ Links
47
+ -----
48
+
49
+ * __Github__ http://github.com/apeiros/autocompletion
50
+ * __Documentation__ http://rdoc.info/github/apeiros/autocompletion/master/frames
51
+ * __Rubygems__ http://rubygems.org/gems/autocompletion
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../rake/lib', __FILE__))
2
+ Dir.glob(File.expand_path('../rake/tasks/**/*.{rake,task,rb}', __FILE__)) do |task_file|
3
+ begin
4
+ import task_file
5
+ rescue LoadError => e
6
+ warn "Failed to load task file #{task_file}"
7
+ warn " #{e.class} #{e.message}"
8
+ warn " #{e.backtrace.first}"
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "autocompletion"
5
+ s.version = "0.0.1"
6
+ s.authors = "Stefan Rusterholz"
7
+ s.email = "stefan.rusterholz@gmail.com"
8
+ s.homepage = "http://github.com/apeiros/autocompletion"
9
+
10
+ s.description = <<-DESCRIPTION.gsub(/^ /, '').chomp
11
+ This gem provides fast prefix-autocompletion in pure ruby.
12
+ DESCRIPTION
13
+
14
+ s.summary = <<-SUMMARY.gsub(/^ /, '').chomp
15
+ Fast prefix-autocompletion in pure ruby.
16
+ SUMMARY
17
+
18
+ s.files =
19
+ Dir['bin/**/*'] +
20
+ Dir['lib/**/*'] +
21
+ Dir['rake/**/*'] +
22
+ Dir['test/**/*'] +
23
+ %w[
24
+ autocompletion.gemspec
25
+ Rakefile
26
+ README.markdown
27
+ ]
28
+
29
+ if File.directory?('bin') then
30
+ executables = Dir.chdir('bin') { Dir.glob('**/*').select { |f| File.executable?(f) } }
31
+ s.executables = executables unless executables.empty?
32
+ end
33
+
34
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1")
35
+ s.rubygems_version = "1.3.1"
36
+ s.specification_version = 3
37
+ end
@@ -0,0 +1,204 @@
1
+ # encoding: utf-8
2
+
3
+
4
+
5
+ require 'autocompletion/version'
6
+
7
+
8
+
9
+ # AutoCompletion
10
+ # A binary search for prefix-matching is used to determine left- and right-
11
+ # boundary. This means even with 1_000_000 items, a maximum of 40 comparisons
12
+ # is required.
13
+ #
14
+ # @example Autocomplete words
15
+ # auto = AutoCompletion.words(%w[foo bar baz])
16
+ # auto.complete('f') # => ["foo"]
17
+ # auto.complete('b') # => ["bar", "baz"]
18
+ # auto.complete('z') # => []
19
+ #
20
+ # @example Autocomplete objects by attributes
21
+ # Person = Struct.new(:first_name, :last_name)
22
+ # people = [
23
+ # Person.new("Peter", "Parker"),
24
+ # Person.new("Luke", "Skywalker"),
25
+ # Person.new("Anakin", "Skywalker"),
26
+ # ]
27
+ # auto = AutoCompletion.map(people) { |person|
28
+ # [person.first_name, person.last_name]
29
+ # }
30
+ #
31
+ # auto.complete("P")
32
+ # # => [#<struct Person first_name="Peter", last_name="Parker">]
33
+ #
34
+ # auto.complete("S")
35
+ # # => [#<struct Person first_name="Luke", last_name="Skywalker">,
36
+ # # #<struct Person first_name="Anakin", last_name="Skywalker">]
37
+ #
38
+ # auto.complete("S", "L")
39
+ # # => [#<struct Person first_name="Luke", last_name="Skywalker">]
40
+ class AutoCompletion
41
+
42
+ # Raised by AutoCompletion::new
43
+ class InvalidOrder < ArgumentError
44
+ def initialize
45
+ super("The prefixes are not in sorted order")
46
+ end
47
+ end
48
+
49
+ # @return [AutoCompletion]
50
+ # An autocompletion for a list of words.
51
+ def self.words(words)
52
+ unordered_tuples(words.map { |word| [word, word] })
53
+ end
54
+
55
+ # @return [AutoCompletion]
56
+ # Map a list of entities to their keys. The block should return an array of valid
57
+ # prefixes for the yielded entity.
58
+ def self.map(entities)
59
+ mapped = entities.flat_map { |entity|
60
+ keys = yield(entity)
61
+ keys.flat_map { |key| [key, entity] }
62
+ }
63
+
64
+ unordered_tuples(mapped.each_slice(2))
65
+ end
66
+
67
+ # @return [AutoCompletion]
68
+ # Creates an AutoCompletion for an unordered array of the form [["prefix", value], …].
69
+ def self.unordered_tuples(entities)
70
+ new(entities.sort_by(&:first).flatten(1), true)
71
+ end
72
+
73
+ # All stored entities. A flat array of the form [prefix1, value1, prefix2, value2, …]
74
+ attr_reader :entities
75
+
76
+ # @param [Array<Array>] entities
77
+ # A flat array of the form [prefix1, value1, prefix2, value2, …] containing all
78
+ # prefixes and their corresponding value.
79
+ # @param [Boolean] force
80
+ # If force is set to true, the order of entities is not verified. Use this only if
81
+ # you know what you're doing.
82
+ #
83
+ # @see AutoCompletion::words, AutoCompletion::map
84
+ def initialize(entities, force=false)
85
+ @entities = entities
86
+ raise InvalidOrder.new unless force || valid?
87
+ end
88
+
89
+ # @return [Boolean]
90
+ # Returns true if the prefixes are in a valid order.
91
+ def valid?
92
+ @entities.each_slice(2).each_cons(2) do |(a,_),(b,_)|
93
+ return false unless a <= b
94
+ end
95
+ true
96
+ end
97
+
98
+ # @return [Boolean]
99
+ # Returns true if there are no prefixes stored in this AutoCompletion instance.
100
+ def empty?
101
+ @entities.empty?
102
+ end
103
+
104
+ # @return [Integer]
105
+ # The number of prefixes stored. Note that the same prefix can occur multiple times.
106
+ def size
107
+ @entities.size>>1
108
+ end
109
+
110
+ # @return [Integer] The number of distinct entities
111
+ def count_distinct_entitites
112
+ result = {}
113
+ @entities.each_slice(2) do |key, value|
114
+ result[value] = true
115
+ end
116
+
117
+ result.size
118
+ end
119
+
120
+ # @return [Integer] The number of distinct prefixes
121
+ def count_distinct_prefixes
122
+ result = {}
123
+ @entities.each_slice(2) do |key, value|
124
+ result[key] = true
125
+ end
126
+
127
+ result.size
128
+ end
129
+
130
+ # @param [String] prefixes
131
+ # A list of prefixes to match. All given prefixes must be matched.
132
+ #
133
+ # @return [Array]
134
+ # Returns an array of distinct entities matching the given prefixes.
135
+ def complete(*prefixes)
136
+ # short-cut
137
+ return [] if empty? || prefixes.any? { |word|
138
+ word < @entities.first[0,word.size] || word > @entities[-2][0,word.size]
139
+ }
140
+
141
+ slices = prefixes.map { |word| range_search(word) }
142
+ return [] if slices.include?(nil) # short-cut
143
+
144
+ result = @entities[slices.pop].each_slice(2).map(&:last).uniq
145
+ slices.each do |slice|
146
+ result &= @entities[slice].each_slice(2).map(&:last)
147
+ end
148
+
149
+ result
150
+ end
151
+
152
+ # @return [nil, Range<Integer>]
153
+ # Returns nil if AutoCompletion#empty?
154
+ # Returns -1..-1 if the prefix is smaller than the smallest key
155
+ # Returns AutoCompletion#size..AutoCompletion#size if the prefix is bigger than the biggest key
156
+ # Returns the range for all matched keys and values otherwise
157
+ def range_search(prefix)
158
+ prefix_size = prefix.size
159
+ length = size()
160
+ found = nil
161
+ left = 0
162
+ right = length-1
163
+ found = false
164
+ max_exc_right = length
165
+
166
+ return nil if empty?
167
+ return -1..-1 if @entities[0][0,prefix_size] > prefix # prefix is smaller than smallest value
168
+ return length..length if @entities[-2][0,prefix_size] < prefix # prefix is bigger than biggest value
169
+
170
+ # binary search for smallest index
171
+ # mark biggest right that include prefix, and biggest mark that doesn't include prefix
172
+ while(left<right)
173
+ index = (left+right)>>1
174
+ cmp_value = @entities.at(index<<1)[0,prefix_size]
175
+ case cmp_value <=> prefix
176
+ when -1 then
177
+ left = index+1
178
+ when 1 then
179
+ right = index
180
+ max_exc_right = right
181
+ else # 0
182
+ right = index
183
+ end
184
+ end
185
+ return nil unless @entities.at(left<<1)[0,prefix_size] == prefix
186
+ final_left = left
187
+
188
+ # binary search for biggest index
189
+ right = max_exc_right-1
190
+ while(left<right)
191
+ index = (left+right)>>1
192
+ cmp_value = @entities.at(index<<1)[0,prefix_size]
193
+ if cmp_value > prefix then
194
+ right = index
195
+ else
196
+ left = index+1
197
+ end
198
+ end
199
+ final_right = right
200
+ final_right -= 1 unless @entities.at(right<<1)[0,prefix_size] == prefix
201
+
202
+ return (final_left<<1)..((final_right<<1)+1)
203
+ end
204
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'rubygems/version' # newer rubygems use this
5
+ rescue LoadError
6
+ require 'gem/version' # older rubygems use this
7
+ end
8
+
9
+ class AutoCompletion
10
+
11
+ # The version of the gem
12
+ Version = Gem::Version.new("0.0.1")
13
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: autocompletion
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stefan Rusterholz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-19 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: This gem provides fast prefix-autocompletion in pure ruby.
15
+ email: stefan.rusterholz@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/autocompletion/version.rb
21
+ - lib/autocompletion.rb
22
+ - autocompletion.gemspec
23
+ - Rakefile
24
+ - README.markdown
25
+ homepage: http://github.com/apeiros/autocompletion
26
+ licenses: []
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>'
41
+ - !ruby/object:Gem::Version
42
+ version: 1.3.1
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 1.8.15
46
+ signing_key:
47
+ specification_version: 3
48
+ summary: Fast prefix-autocompletion in pure ruby.
49
+ test_files: []