briskly 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c1e4055da36518f843ffa0a729ec7ab016d2ab9d
4
+ data.tar.gz: cd3a5302b93311c7639379865fcf20d9293e1089
5
+ SHA512:
6
+ metadata.gz: 7256bcc2ba6efe086617b0b0fff4d607396ab013ff702bdb28534134d865fe8a1ab89dca3ca4e46706a2c67d42324cde543771ada1c05204c75c277165c2fe5e
7
+ data.tar.gz: 454ee238cf15ffdf96416f66d8d8b8acc8dfc2b2014445492882105e39840172cfe588e0be7c05c5f9d504ba29922e6825c44982f77b186e3091fff294a63ee5
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .ruby-version
2
+ *.gem
3
+ tmp/*
4
+ .bundle/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ script:
7
+ - 'bundle exec rspec'
8
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source ENV.fetch('GEM_SOURCE', 'https://rubygems.org')
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,51 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ briskly (0.1.1)
5
+ fast_trie
6
+ i18n
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ coderay (1.1.0)
12
+ diff-lcs (1.2.5)
13
+ fast_trie (0.5.0)
14
+ ffi (1.9.3)
15
+ guard (0.10.0)
16
+ ffi (>= 0.5.0)
17
+ thor (~> 0.14.6)
18
+ guard-rspec (0.7.3)
19
+ guard (>= 0.10.0)
20
+ i18n (0.6.9)
21
+ method_source (0.8.2)
22
+ pry (0.9.12.6)
23
+ coderay (~> 1.0)
24
+ method_source (~> 0.8)
25
+ slop (~> 3.4)
26
+ pry-nav (0.2.3)
27
+ pry (~> 0.9.10)
28
+ rake (10.2.2)
29
+ rspec (2.14.1)
30
+ rspec-core (~> 2.14.0)
31
+ rspec-expectations (~> 2.14.0)
32
+ rspec-mocks (~> 2.14.0)
33
+ rspec-core (2.14.8)
34
+ rspec-expectations (2.14.5)
35
+ diff-lcs (>= 1.1.3, < 2.0)
36
+ rspec-mocks (2.14.6)
37
+ slop (3.5.0)
38
+ thor (0.14.6)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ briskly!
45
+ bundler (~> 1.4)
46
+ guard (~> 0)
47
+ guard-rspec (~> 0)
48
+ pry (~> 0)
49
+ pry-nav (~> 0)
50
+ rake (~> 10.0)
51
+ rspec (~> 2.4)
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Pedro Cunha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,94 @@
1
+ briskly [![Build Status](https://travis-ci.org/pedrocunha/briskly.svg?branch=master)](https://travis-ci.org/pedrocunha/briskly) [![Code Climate](https://codeclimate.com/github/pedrocunha/briskly.png)](https://codeclimate.com/github/pedrocunha/briskly)
2
+ =====
3
+
4
+ ### Usage:
5
+
6
+ #### Storing:
7
+
8
+ You can store a collection within a specific key. The data must be an array of hashes with a keyword and an optional data argument. Keyword can
9
+ be an array.
10
+
11
+ ```ruby
12
+ Briskly.store('cities').with([
13
+ { keyword: ['London', 'Londres'], data: { id: 10, name: London } },
14
+ { keyword: 'Berlin', data: { id: 15, name: Berlin } },
15
+ { keyword: 'Barcelona', data: { id: 25, name: Barcelona } }
16
+ )]
17
+ ```
18
+
19
+ - Elements are returned in the order they were inserted
20
+ - Re-using the same key **overrides** the existing collection
21
+
22
+
23
+
24
+ #### Searching:
25
+
26
+ Search on collections using `#on`. The result is composed by an hash with the key(s) requested
27
+ and the values are instances of `Briskly::Element` class. This object responds to `#keyword` and `#data`.
28
+
29
+ ```ruby
30
+ Briskly.store('cities').with([
31
+ { keyword: ['London', 'Londres'], data: { id: 10, name: London } },
32
+ { keyword: 'Berlin', data: { id: 15, name: Berlin } },
33
+ { keyword: 'Barcelona', data: { id: 25, name: Barcelona } }
34
+ )]
35
+
36
+ result = Briskly.on('cities').search('lon')
37
+
38
+ result
39
+ => { 'cities' => [ #Briskly::Element ... ]
40
+
41
+ result['cities'].first.keyword
42
+ => 'London'
43
+
44
+
45
+ result = Briskly.on('cities').search('londres')
46
+ result['cities'].first.keyword
47
+ => 'Londres'
48
+
49
+ result['cities'].first.alternatives
50
+ => ['London']
51
+
52
+ ```
53
+
54
+ Example with multiple collections
55
+
56
+ ```ruby
57
+ Briskly.store('cities').with([
58
+ { keyword: 'Foo' },
59
+ ...
60
+ )]
61
+
62
+ Briskly.store('countries').with([
63
+ { keyword: 'Foobear' },
64
+ ...
65
+ )]
66
+
67
+ result = Briskly.on('countries', 'cities').search('foo')
68
+
69
+ result
70
+ => { 'cities' => [ #Briskly::Element ], 'countries' => [ #Briskly::Element ] }
71
+ ```
72
+
73
+ Notes:
74
+ - Search is not case sensitive
75
+ - Results are returned only if they match beginning of word
76
+
77
+
78
+
79
+ Note on Patches/Pull Requests
80
+ =============================
81
+
82
+ * Fork the project.
83
+ * Make your feature addition or bug fix.
84
+ * Add tests for it. This is important so I don't break it in a
85
+ future version unintentionally.
86
+ * Commit, do not mess with rakefile, version, or history.
87
+ (if you want to have your own version, that is fine but
88
+ bump version in a commit by itself I can ignore when I pull)
89
+ * Send me a pull request. Bonus points for topic branches.
90
+
91
+ License
92
+ ============
93
+ MIT licence. Copyright (c) 2013 HouseTrip Ltd.
94
+
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env rake
2
+ # encoding: UTF-8
3
+
4
+ require 'bundler/gem_tasks'
data/briskly.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../lib/briskly/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'briskly'
5
+ gem.authors = 'Pedro Cunha'
6
+ gem.email = 'pkunha@gmail.com'
7
+ gem.description = %q{An in-memory autocomplete}
8
+ gem.summary = %q{
9
+ An left-hand search in-memory autocomplete wrapper around
10
+ a C extension for fast searches
11
+ }
12
+ gem.homepage = 'http://github.com/pedrocunha/briskly'
13
+ gem.license = 'MIT'
14
+ gem.files = `git ls-files`.split($\)
15
+ gem.require_paths = ['lib']
16
+ gem.version = Briskly::VERSION
17
+
18
+ gem.add_dependency 'i18n'
19
+ gem.add_dependency 'fast_trie'
20
+
21
+ gem.add_development_dependency 'bundler', '~> 1.4'
22
+ gem.add_development_dependency 'rake', '~> 10.0'
23
+ gem.add_development_dependency 'rspec', '~> 2.4'
24
+ gem.add_development_dependency 'pry', '~> 0'
25
+ gem.add_development_dependency 'pry-nav', '~> 0'
26
+ gem.add_development_dependency 'guard', '~> 0'
27
+ gem.add_development_dependency 'guard-rspec', '~> 0'
28
+ end
data/lib/briskly.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'briskly/version'
2
+
3
+ module Briskly
4
+ @@storage = {}
5
+
6
+ module_function
7
+
8
+ def store(key)
9
+ @@storage[key] = Briskly::Store.new(key)
10
+ end
11
+
12
+ def on(*keys)
13
+ stores = [keys].flatten.map { |key| @@storage[key] || Briskly::Store.new(key) }
14
+ Briskly::Scope.new stores
15
+ end
16
+ end
17
+
18
+ require 'briskly/store'
19
+ require 'briskly/scope'
Binary file
@@ -0,0 +1,22 @@
1
+ require 'briskly'
2
+ require 'briskly/keyword'
3
+
4
+ class Briskly::Element
5
+
6
+ attr_reader :data
7
+ attr_reader :keyword
8
+ attr_reader :alternatives
9
+
10
+ def initialize(keyword, data = nil, alternatives = [])
11
+ raise ArgumentError unless keyword
12
+
13
+ @keyword = Briskly::Keyword.new(keyword)
14
+ @data = data
15
+ @alternatives = alternatives.map { |alternative| Briskly::Keyword.new(alternative) }
16
+ end
17
+
18
+ def keyword(internal = nil)
19
+ internal == :internal ? @keyword : @keyword.to_s
20
+ end
21
+
22
+ end
@@ -0,0 +1,23 @@
1
+ require 'briskly'
2
+ require 'i18n'
3
+
4
+ class Briskly::Keyword
5
+
6
+ def initialize(keyword)
7
+ @keyword = keyword
8
+ end
9
+
10
+ def to_s
11
+ @keyword
12
+ end
13
+
14
+ def normalised
15
+ @_normalised ||= begin
16
+ I18n.transliterate(@keyword)
17
+ .downcase
18
+ .gsub(/[^a-z -]/, '')
19
+ .gsub(/[\s-]+/, ' ')
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,20 @@
1
+ require 'briskly'
2
+
3
+ class Briskly::Scope
4
+
5
+ attr_reader :stores
6
+
7
+ def initialize(stores)
8
+ @stores = stores
9
+ end
10
+
11
+ def search(keyword, options = {})
12
+ result = {}
13
+
14
+ @stores.each do |store|
15
+ result[store.key] = store.search(keyword, options)
16
+ end
17
+
18
+ result
19
+ end
20
+ end
@@ -0,0 +1,78 @@
1
+ require 'briskly'
2
+ require 'briskly/element'
3
+ require 'trie'
4
+
5
+ class Briskly::Store
6
+
7
+ attr_reader :key
8
+
9
+ def initialize(key)
10
+ @key = key
11
+ @store = Trie.new
12
+ @elements = {}
13
+ end
14
+
15
+ def with(values)
16
+ @store = Trie.new
17
+ @elements = {}
18
+
19
+ values.each_with_index do |value, index|
20
+
21
+ keywords = Array.new(1) { value[:keyword] }.flatten(1)
22
+
23
+ keywords.each do |keyword|
24
+ alternatives = keywords - [ keyword ]
25
+ element = Briskly::Element.new(keyword, value[:data], alternatives)
26
+ normalised = element.keyword(:internal).normalised
27
+
28
+ # We need to make sure we keep the index
29
+ # and in order to avoid loops always order
30
+ # the array after each insertion
31
+ @elements[normalised] ||= []
32
+ @elements[normalised].push([element, index])
33
+ .sort! { |a,b| a[1] <=> b[1] }
34
+ end
35
+
36
+ end
37
+
38
+
39
+ @elements.each do |key, values|
40
+ @store.add key, values
41
+ end
42
+ end
43
+
44
+ def search(keyword, options = {})
45
+ keyword = Briskly::Keyword.new(keyword)
46
+
47
+ result = @store.children_with_values(keyword.normalised)
48
+ .map(&:last)
49
+ .flatten(1)
50
+ .sort{ |a, b| a[1] <=> b[1] }
51
+
52
+
53
+ limit = options.fetch(:limit, result.length)
54
+ counter = 0
55
+ output = []
56
+ related = []
57
+
58
+
59
+ # If n elements have the same index that
60
+ # means they are related. Trie will give
61
+ # the best keyword match on it's first
62
+ # position so we should ignore the others
63
+ #
64
+ # `related` keeps the list of related keywords
65
+ result.each do |element|
66
+ next if related[element[1]]
67
+ related[element[1]] = true
68
+
69
+ output << element[0]
70
+
71
+ counter += 1
72
+ break if counter == limit
73
+ end
74
+
75
+ output
76
+ end
77
+ end
78
+
@@ -0,0 +1,6 @@
1
+ require 'pathname'
2
+
3
+ module Briskly
4
+ VERSION = '0.1.1'
5
+ GEMDIR = Pathname.new(__FILE__).parent.parent.parent
6
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'briskly/element'
3
+
4
+ describe Briskly::Element do
5
+
6
+ context 'one keyword' do
7
+
8
+ subject { described_class.new('foo', { a: 2 }) }
9
+
10
+ describe '#new' do
11
+ it 'accepts a keyword and data' do
12
+ expect(subject).to be_a described_class
13
+ end
14
+
15
+ it 'allows access to the keyword' do
16
+ expect(subject.keyword).to eq('foo')
17
+ end
18
+
19
+ it 'allows access to the internal keyword instance' do
20
+ expect(subject.keyword(:internal)).to be_a Briskly::Keyword
21
+ end
22
+
23
+ it 'allows access to its data' do
24
+ expect(subject.data).to eql(a: 2)
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ context 'alternatives' do
31
+ subject { described_class.new('foo', { a: 2 }, ['bar', 'bear']) }
32
+
33
+ it 'converts alternatives to keyword elements' do
34
+ expect(subject.alternatives.first.to_s).to eql('bar')
35
+ end
36
+ end
37
+
38
+ context 'without keywords' do
39
+ it 'raises ArgumentError' do
40
+ expect { described_class.new }.to raise_error(ArgumentError)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+ require 'briskly/keyword'
5
+
6
+ describe Briskly::Keyword do
7
+
8
+ describe '#to_s' do
9
+ subject { described_class.new('foo') }
10
+
11
+ it 'returns the keyword' do
12
+ expect(subject.to_s).to eq('foo')
13
+ end
14
+ end
15
+
16
+ describe '#normalised' do
17
+ subject { described_class.new('foo-bar-ão') }
18
+
19
+ it 'removes accents and dashes' do
20
+ expect(subject.normalised).to eq('foo bar ao')
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'briskly/scope'
3
+
4
+ describe Briskly::Scope do
5
+
6
+ let(:results) { [{ foo: 'bar' }] }
7
+ let(:store) { double('Store', key: 'en:foo', search: results) }
8
+ let(:stores) { [store] }
9
+
10
+ subject { described_class.new(stores) }
11
+
12
+ it 'initializes with stores' do
13
+ expect(subject).to be_a described_class
14
+ end
15
+
16
+ describe '#search' do
17
+
18
+ it 'returns an hash with the store key' do
19
+ expect(subject.search('foo').keys).to eql(['en:foo'])
20
+ end
21
+
22
+ it 'returns the search result' do
23
+ expect(subject.search('foo')['en:foo'].first).to eql({ foo: 'bar' })
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,227 @@
1
+ require 'spec_helper'
2
+ require 'briskly/store'
3
+
4
+ describe Briskly::Store do
5
+
6
+ describe '#new' do
7
+
8
+ it 'accepts a store name as argument' do
9
+ expect(described_class.new('en:foo').key).to eql('en:foo')
10
+ end
11
+
12
+ it 'raises exception with multiple arguments' do
13
+ expect{ described_class.new('en:foo', 'en:bar') }.to raise_error
14
+ end
15
+ end
16
+
17
+ describe '#with' do
18
+
19
+ subject { described_class.new('en:foo') }
20
+
21
+ it 'stores an element' do
22
+ expect(subject.with [{ keyword: 'foo', data: { :id => 100 } }]).to be_true
23
+ end
24
+
25
+ it 'data argument is optional' do
26
+ expect(subject.with [{ keyword: 'foo' }]).to be_true
27
+ end
28
+
29
+ it 'keywords argument is mandatory' do
30
+ expect{subject.with [{ data: 'foo' }]}.to raise_error
31
+ end
32
+
33
+ it 'stores an element with an array of keywords' do
34
+ expect{subject.with [{ keyword: ['foo', 'bar'], data: 'foo' }]}.to_not raise_error
35
+ end
36
+ end
37
+
38
+ describe '#search' do
39
+
40
+ subject { described_class.new('en:foo') }
41
+
42
+
43
+ context 'limiting results' do
44
+
45
+ before do
46
+ subject.with([
47
+ { keyword: 'foo' },
48
+ { keyword: 'foobear' },
49
+ { keyword: 'foobaz' },
50
+ { keyword: 'fooyolo' },
51
+ { keyword: 'foocorse'},
52
+ { keyword: 'foopizza'}
53
+ ])
54
+ end
55
+
56
+ it 'returns only five elements' do
57
+ result = subject.search('foo', limit: 5)
58
+ expect(result).to have(5).items
59
+ end
60
+
61
+ it 'returns all results' do
62
+ result = subject.search('foo')
63
+ expect(result).to have(6).items
64
+ end
65
+
66
+ end
67
+
68
+ context 'multiple results' do
69
+
70
+ before do
71
+ subject.with([
72
+ { keyword: 'london' },
73
+ { keyword: 'lon' },
74
+ { keyword: 'londa' },
75
+ { keyword: 'lonato' },
76
+ { keyword: 'bear' }
77
+ ])
78
+ end
79
+
80
+ let(:result) { subject.search('lon') }
81
+
82
+ it 'returns all matching results' do
83
+ expect(result.length).to eql(4)
84
+ end
85
+
86
+ it 'respects the order of insertion' do
87
+ expect(result.map(&:keyword)).to eql(['london', 'lon', 'londa', 'lonato'])
88
+ end
89
+ end
90
+
91
+ context 'attached data' do
92
+ it 'returns respective data' do
93
+ subject.with([ keyword: 'bob', data: 1 ])
94
+ expect(subject.search('bob').first.data).to eql(1)
95
+ end
96
+ end
97
+
98
+ context 'ignores case-sensitive' do
99
+ it 'ignores case on the keyword' do
100
+ subject.with([ keyword: 'bob' ])
101
+ expect(subject.search('Bob').first.keyword).to eql('bob')
102
+ end
103
+
104
+ it 'ignores case on the keywords' do
105
+ subject.with([ keyword: 'Bob' ])
106
+ expect(subject.search('bob').first.keyword).to eql('Bob')
107
+ end
108
+ end
109
+
110
+ context 'matching partially keywords' do
111
+
112
+ it 'returns a result if keyword length is smaller than keywords chars' do
113
+ subject.with([ keyword: 'bob_is_nice' ])
114
+ expect(subject.search('Bob').first.keyword).to eql('bob_is_nice')
115
+ end
116
+
117
+ it 'does not return a result if keyword length is bigger than keywords chars' do
118
+ subject.with([ keyword: 'bob' ])
119
+ expect(subject.search('BobIsNice')).to be_empty
120
+ end
121
+
122
+ end
123
+
124
+ context 'handling empty store' do
125
+ it 'returns empty array' do
126
+ expect(subject.search('foo')).to be_empty
127
+ end
128
+ end
129
+
130
+ context 'overriding data' do
131
+
132
+ before do
133
+ subject.with([ keyword: 'foo', data: 1])
134
+ subject.with([ keyword: 'foo', data: 2])
135
+ end
136
+
137
+ it 'returns only 1 element' do
138
+ expect(subject.search('foo')).to have(1).item
139
+ end
140
+
141
+ it 'returns the right data' do
142
+ expect(subject.search('foo').first.data).to eql(2)
143
+ end
144
+
145
+ end
146
+
147
+ context 'same keywords' do
148
+ before do
149
+ subject.with([
150
+ { keyword: 'foo', data: 1 },
151
+ { keyword: 'foo', data: 2 }
152
+ ])
153
+ end
154
+
155
+ let(:result) { subject.search('foo') }
156
+
157
+ it 'returns 2 results' do
158
+ expect(result).to have(2).items
159
+ end
160
+
161
+ it 'respects order' do
162
+ expect(result.map(&:data)).to eql [1, 2]
163
+ end
164
+ end
165
+
166
+ context 'multiple keywords' do
167
+ before do
168
+ subject.with([
169
+ { keyword: ['foo', 'bar'], data: 1 },
170
+ { keyword: 'bear', data: 2 }
171
+ ])
172
+ end
173
+
174
+ let(:result) { subject.search('bar') }
175
+
176
+ it 'returns 1 result for bar' do
177
+ expect(result).to have(1).item
178
+ end
179
+
180
+ it 'returns data equal to 1' do
181
+ expect(result.map(&:data)).to eql [1]
182
+ end
183
+
184
+ end
185
+
186
+ context 'multiple keywords with similar names' do
187
+ before do
188
+ subject.with([
189
+ { keyword: ['foo', 'foobar'] }
190
+ ])
191
+ end
192
+
193
+ let(:result) { subject.search('foo') }
194
+
195
+ it 'returns 1 result for foo' do
196
+ expect(result).to have(1).item
197
+ end
198
+
199
+ it 'returns the element with keyword foo' do
200
+ expect(result.map(&:keyword)).to eql ['foo']
201
+ end
202
+
203
+ end
204
+
205
+ context 'multiple keywords with similar names but different elements' do
206
+ before do
207
+ subject.with([
208
+ { keyword: ['foo', 'foobar'], data: 1 },
209
+ { keyword: ['foobar'], data: 2 }
210
+ ])
211
+ end
212
+
213
+ let(:result) { subject.search('foo') }
214
+
215
+ it 'returns 2 result for foo' do
216
+ expect(result).to have(2).item
217
+ end
218
+
219
+ it 'returns data' do
220
+ expect(result.map(&:data)).to eql [1, 2]
221
+ end
222
+
223
+ end
224
+
225
+ end
226
+
227
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require 'briskly'
3
+
4
+ describe Briskly, 'integration' do
5
+
6
+ before do
7
+ described_class.store('en:foo').with([
8
+ { keyword: ['foo', 'bear'] },
9
+ { keyword: 'foobear' }
10
+ ])
11
+ end
12
+
13
+ context 'searching single collections' do
14
+
15
+ subject { described_class.on('en:foo').search('foo') }
16
+
17
+ it 'returns matching values for en:foo collection' do
18
+ expect(subject['en:foo'].map(&:keyword)).to eql(['foo', 'foobear'])
19
+ end
20
+
21
+ end
22
+
23
+ context 'alternatives' do
24
+
25
+ subject { described_class.on('en:foo').search('bear') }
26
+
27
+ it 'matches the alternative' do
28
+ expect(subject['en:foo'].map(&:keyword)).to eql(['bear'])
29
+ end
30
+
31
+ end
32
+
33
+ context 'limiting results' do
34
+
35
+ it 'returns only 1 element' do
36
+ subject = described_class.on('en:foo').search('foo', limit: 1)
37
+ expect(subject['en:foo']).to have(1).item
38
+ end
39
+
40
+ it 'returns all results' do
41
+ subject = described_class.on('en:foo').search('foo')
42
+ expect(subject['en:foo']).to have(2).items
43
+ end
44
+
45
+ end
46
+
47
+
48
+ context 'searching multiple collections' do
49
+ before do
50
+ described_class.store('en:bar').with([
51
+ { keyword: 'bar' },
52
+ { keyword: 'bar-bear' },
53
+ { keyword: 'foo' }
54
+ ])
55
+ end
56
+
57
+ subject { described_class.on('en:foo', 'en:bar').search('foo') }
58
+
59
+ it 'returns 2 collections' do
60
+ expect(subject.keys.length).to eql(2)
61
+ end
62
+
63
+ it 'returns matching values for en:foo collection' do
64
+ expect(subject['en:foo'].map(&:keyword)).to eql(['foo', 'foobear'])
65
+ end
66
+
67
+ it 'returns matching values for en:bar collection' do
68
+ expect(subject['en:bar'].map(&:keyword)).to eql(['foo'])
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,13 @@
1
+ require 'pry'
2
+
3
+ RSpec.configure do |config|
4
+ config.treat_symbols_as_metadata_keys_with_true_values = true
5
+ config.run_all_when_everything_filtered = true
6
+ config.filter_run :focus
7
+
8
+ # Run specs in random order to surface order dependencies. If you find an
9
+ # order dependency and want to debug it, you can fix the order by providing
10
+ # the seed, which is printed after each run.
11
+ # --seed 1234
12
+ config.order = 'random'
13
+ end
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: briskly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Pedro Cunha
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: i18n
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fast_trie
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-nav
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: An in-memory autocomplete
140
+ email: pkunha@gmail.com
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - ".gitignore"
146
+ - ".rspec"
147
+ - ".travis.yml"
148
+ - Gemfile
149
+ - Gemfile.lock
150
+ - Guardfile
151
+ - LICENSE
152
+ - README.md
153
+ - Rakefile
154
+ - briskly.gemspec
155
+ - lib/briskly.rb
156
+ - lib/briskly/briskly.bundle
157
+ - lib/briskly/element.rb
158
+ - lib/briskly/keyword.rb
159
+ - lib/briskly/scope.rb
160
+ - lib/briskly/store.rb
161
+ - lib/briskly/version.rb
162
+ - spec/briskly/element_spec.rb
163
+ - spec/briskly/keyword_spec.rb
164
+ - spec/briskly/scope_spec.rb
165
+ - spec/briskly/store_spec.rb
166
+ - spec/briskly_spec.rb
167
+ - spec/spec_helper.rb
168
+ homepage: http://github.com/pedrocunha/briskly
169
+ licenses:
170
+ - MIT
171
+ metadata: {}
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubyforge_project:
188
+ rubygems_version: 2.2.0
189
+ signing_key:
190
+ specification_version: 4
191
+ summary: An left-hand search in-memory autocomplete wrapper around a C extension for
192
+ fast searches
193
+ test_files: []