briskly 0.1.1

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