elasticsearch_autocomplete 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ .idea
20
+ .rvmrc
21
+ .rspec
22
+ TODO
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in elasticsearch_autocomplete.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Alex Leschenko
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # ElasticsearchAutocomplete
2
+
3
+ Simple autocomplete for rails models using awesome elasticsearch and tire gem
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'elasticsearch_autocomplete'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install elasticsearch_autocomplete
18
+
19
+ ## Basic Usage
20
+
21
+ Specify attributes for autocompletion. By default, this is `name` attribute:
22
+
23
+ ```ruby
24
+ class User < ActiveRecord::Base
25
+ ac_field :full_name
26
+ end
27
+ ```
28
+
29
+ To find suggestions call `ac_search` method on your model. It return `Tire::Results::Collection` instance:
30
+
31
+ ```ruby
32
+ User.ac_search('Alex').map(&:full_name)
33
+ => ['Alex First', 'Alexandr Second']
34
+ ```
35
+
36
+ ##
37
+
38
+ You can specify fields for suggestions search:
39
+
40
+ ```ruby
41
+ class User < ActiveRecord::Base
42
+ ac_field :full_name, :search_fields => [:full_name, :email]
43
+ end
44
+ ```
45
+
46
+ For search on localized fields such as `name_en`, `name_ru`:
47
+
48
+ ```ruby
49
+ class Product < ActiveRecord::Base
50
+ ac_field :name, :localized => true
51
+ end
52
+ ```
53
+
54
+ If you wand to define settings and mapping for elasticsearch index yourselves:
55
+
56
+ ```ruby
57
+ class Product < ActiveRecord::Base
58
+ ac_field :name, :skip_settings => true
59
+ end
60
+ ```
61
+
62
+ ## Contributing
63
+
64
+ 1. Fork it
65
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
66
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
67
+ 4. Push to the branch (`git push origin my-new-feature`)
68
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'elasticsearch_autocomplete/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'elasticsearch_autocomplete'
8
+ gem.version = ElasticsearchAutocomplete::VERSION
9
+ gem.authors = ['Alex Leschenko']
10
+ gem.email = %w(leschenko.al@gmail.com)
11
+ gem.summary = %q{Elasticsearch autocomplete for models}
12
+ gem.description = %q{Simple autocomplete for your models using awesome elasticsearch and tire gem}
13
+ gem.homepage = 'https://github.com/leschenko/elasticsearch_autocomplete'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = %w(lib)
19
+
20
+ gem.add_dependency 'tire'
21
+
22
+ gem.add_development_dependency 'rake'
23
+ gem.add_development_dependency 'rspec'
24
+ gem.add_development_dependency 'oj'
25
+ end
@@ -0,0 +1,59 @@
1
+ module ElasticsearchAutocomplete
2
+ module Analyzers
3
+
4
+ AC_TOKENIZERS = {
5
+ :ac_edge_ngram => {
6
+ :type => 'edgeNGram',
7
+ :min_gram => 1,
8
+ :max_gram => 50,
9
+ :side => 'front'
10
+ },
11
+ :ac_edge_ngram_full => {
12
+ :type => 'nGram',
13
+ :min_gram => 1,
14
+ :max_gram => 50
15
+ }
16
+ }
17
+
18
+ AC_FILTERS = {
19
+ :ac_edge_ngram => {
20
+ :type => 'edgeNGram',
21
+ :min_gram => 1,
22
+ :max_gram => 50,
23
+ :side => 'front'
24
+ }
25
+ }
26
+
27
+ AC_ANALYZERS = {
28
+ :ac_edge_ngram => {
29
+ :type => 'custom',
30
+ :tokenizer => 'ac_edge_ngram',
31
+ :filter => %w(lowercase asciifolding)
32
+ },
33
+ :ac_edge_ngram_full => {
34
+ :type => 'custom',
35
+ :tokenizer => 'ac_edge_ngram_full',
36
+ :filter => %w(lowercase asciifolding)
37
+ },
38
+ :ac_edge_ngram_word => {
39
+ :type => 'custom',
40
+ :tokenizer => 'standard',
41
+ :filter => %w(lowercase asciifolding ac_edge_ngram)
42
+ },
43
+ :ac_search => {
44
+ :type => 'custom',
45
+ :tokenizer => 'keyword',
46
+ :filter => %w(lowercase asciifolding)
47
+ }
48
+ }
49
+
50
+ AC_BASE = {
51
+ :analysis => {
52
+ :analyzer => AC_ANALYZERS,
53
+ :tokenizer => AC_TOKENIZERS,
54
+ :filter => AC_FILTERS
55
+ }
56
+ }
57
+
58
+ end
59
+ end
@@ -0,0 +1,125 @@
1
+ module ElasticsearchAutocomplete
2
+ module ModelAddition
3
+ def self.included(base)
4
+ base.send :extend, SingletonMethods
5
+ end
6
+
7
+ module SingletonMethods
8
+ def elasticsearch(options={})
9
+ include Tire::Model::Search
10
+ unless options.delete(:skip_after_save)
11
+ after_save lambda { tire.update_index }
12
+ end
13
+ after_destroy lambda { tire.update_index }
14
+ index_prefix ElasticsearchAutocomplete.defaults[:index_prefix]
15
+ end
16
+
17
+ def ac_field(*args, &block)
18
+ options = args.extract_options!
19
+
20
+ include InstanceMethods
21
+ extend ClassMethods
22
+
23
+ elasticsearch(options)
24
+
25
+ class_attribute :ac_opts, :ac_attr, :instance_writer => false
26
+ self.ac_opts = options.reverse_merge(ElasticsearchAutocomplete.defaults)
27
+ self.ac_attr = args.first || ElasticsearchAutocomplete.defaults[:attr]
28
+
29
+ define_ac_index(ac_opts[:mode]) unless options[:no_settings]
30
+ end
31
+ end
32
+
33
+ module ClassMethods
34
+ def ac_search(query, options={})
35
+ options.reverse_merge!({:per_page => 50, :search_fields => ac_search_fields})
36
+
37
+ tire.search :per_page => options[:per_page] do
38
+ query do
39
+ if query.size.zero?
40
+ all
41
+ else
42
+ match options[:search_fields], query
43
+ end
44
+ end
45
+
46
+ sort { by options[:order], options[:sort_mode] || 'asc' } if options[:order].present?
47
+
48
+ filter(:and, :filters => options[:with].map { |k, v| {:terms => {k => ElasticsearchAutocomplete.val_to_array(v)}} }) if options[:with].present?
49
+ if options[:without].present?
50
+ options[:without].each do |k, v|
51
+ filter(:not, {:terms => {k => ElasticsearchAutocomplete.val_to_array(v, true)}})
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def ac_search_attrs
58
+ @ac_search_attrs ||=
59
+ if ac_opts[:search_fields]
60
+ ac_opts[:search_fields]
61
+ else
62
+ if ac_opts[:localized]
63
+ I18n.available_locales.map { |l| "#{ac_attr}_#{l}" }
64
+ else
65
+ [ac_attr]
66
+ end
67
+ end
68
+ end
69
+
70
+ def for_input_token(r, attr='name_ru')
71
+ {:name => r[attr], :id => r.id}
72
+ end
73
+
74
+ def ac_mode_config
75
+ ElasticsearchAutocomplete::MODES[ac_opts[:mode]]
76
+ end
77
+
78
+ private
79
+
80
+ def define_ac_index(mode=:word)
81
+ mode_config = ac_mode_config
82
+ settings ElasticsearchAutocomplete::Analyzers::AC_BASE do
83
+ mapping do
84
+ ac_search_attrs.each do |attr|
85
+ case mode
86
+ when :word
87
+ indexes attr, :type => 'multi_field', :fields => {
88
+ attr => {:type => 'string'},
89
+ "#{mode_config[:base]}_#{attr}" => {:type => 'string', :index_analyzer => 'ac_edge_ngram', :search_analyzer => 'ac_search', :include_in_all => false},
90
+ "#{mode_config[:word]}_#{attr}" => {:type => 'string', :index_analyzer => 'ac_edge_ngram_word', :search_analyzer => 'ac_search', :include_in_all => false}
91
+ }
92
+ when :phrase
93
+ indexes attr, :type => 'multi_field', :fields => {
94
+ attr => {:type => 'string'},
95
+ "#{mode_config[:base]}_#{attr}" => {:type => 'string', :index_analyzer => 'ac_edge_ngram', :search_analyzer => 'ac_search', :include_in_all => false},
96
+ }
97
+ when :full
98
+ indexes attr, :type => 'multi_field', :fields => {
99
+ attr => {:type => 'string'},
100
+ "#{mode_config[:base]}_#{attr}" => {:type => 'string', :index_analyzer => 'ac_edge_ngram', :search_analyzer => 'ac_search', :include_in_all => false, :boost => 3},
101
+ "#{mode_config[:full]}_#{attr}" => {:type => 'string', :index_analyzer => 'ac_edge_ngram_full', :search_analyzer => 'ac_search', :include_in_all => false}
102
+ }
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def ac_search_fields
110
+ @ac_search_fields ||= ac_search_attrs.map { |attr| ac_mode_config.values.map { |prefix| "#{prefix}_#{attr}" } }.flatten
111
+ end
112
+ end
113
+
114
+ module InstanceMethods
115
+ def to_indexed_json
116
+ for_json = {}
117
+ attrs = [:id, :created_at] + self.class.ac_search_attrs
118
+ attrs.each do |attr|
119
+ for_json[attr] = send(attr)
120
+ end
121
+ MultiJson.encode(for_json)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,9 @@
1
+ module ElasticsearchAutocomplete
2
+ class Railtie < Rails::Railtie
3
+ initializer 'bit_mask.model_additions' do
4
+ ActiveSupport.on_load :active_record do
5
+ include ElasticsearchAutocomplete::ModelAddition
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module ElasticsearchAutocomplete
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,26 @@
1
+ require 'elasticsearch_autocomplete/version'
2
+ require 'elasticsearch_autocomplete/analyzers'
3
+ require 'elasticsearch_autocomplete/model_addition'
4
+ require 'elasticsearch_autocomplete/railtie' if defined? Rails
5
+
6
+ module ElasticsearchAutocomplete
7
+ mattr_accessor :defaults
8
+
9
+ def self.default_index_prefix
10
+ Rails.application.class.name.split('::').first.downcase if Object.const_defined?('Rails')
11
+ end
12
+
13
+ self.defaults = {:attr => :name, :localized => false, :mode => :word, :index_prefix => default_index_prefix}
14
+
15
+ MODES = {
16
+ :word => {:base => 'ac', :word => 'ac_word'},
17
+ :phrase => {:base => 'ac'},
18
+ :full => {:base => 'ac', :full => 'ac_full'}
19
+ }
20
+
21
+ def self.val_to_array(val, zero=false)
22
+ return [] unless val
23
+ a = val.is_a?(Array) ? val : val.to_s.split(',').map(&:to_i)
24
+ zero ? a : a.reject(&:zero?)
25
+ end
26
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ class ActiveModelUser < StubModelBase
4
+ ac_field :full_name
5
+ end
6
+
7
+ describe ElasticsearchAutocomplete do
8
+ subject { ActiveModelUser }
9
+ before :all do
10
+ ActiveModelUser.setup_index
11
+ end
12
+
13
+ it 'add ac_search method' do
14
+ should respond_to(:ac_search)
15
+ end
16
+
17
+ it 'define to_indexed_json method' do
18
+ ActiveModelUser.new(:full_name => 'test').to_indexed_json.should == '{"id":null,"created_at":null,"full_name":"test"}'
19
+ end
20
+
21
+ describe 'default settings' do
22
+ around do |example|
23
+ old_settings = ElasticsearchAutocomplete.defaults
24
+ example.run
25
+ ElasticsearchAutocomplete.defaults = old_settings
26
+ end
27
+
28
+ it 'allow to change default settings' do
29
+ ElasticsearchAutocomplete.defaults = {:attr => :test, :localized => true, :mode => :phrase, :index_prefix => 'test'}
30
+ ElasticsearchAutocomplete.defaults.should == {:attr => :test, :localized => true, :mode => :phrase, :index_prefix => 'test'}
31
+ end
32
+ end
33
+ end
34
+
35
+ shared_examples 'basic autocomplete' do |model|
36
+ it 'suggest for beginning of the source' do
37
+ model.ac_search('Joyce Flores').to_a.should_not be_empty
38
+ end
39
+
40
+ it 'suggest for for full match' do
41
+ model.ac_search('Lau').to_a.should_not be_empty
42
+ end
43
+
44
+ it 'don\'t suggest for unmatched term' do
45
+ model.ac_search('Lai').to_a.should be_empty
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ class ActiveModelProductFull < StubModelBase
4
+ ac_field :sku, :mode => :full
5
+
6
+ def self.test_data
7
+ ['SAMARA', 'A.3103', 'b A.3611', 'kac12 dk/sm']
8
+ end
9
+
10
+ def self.populate
11
+ test_data.each_with_index do |name, id|
12
+ u = new(:sku => name)
13
+ u.id = id
14
+ u.save
15
+ end
16
+ end
17
+ end
18
+
19
+ describe ':full mode autocomplete' do
20
+ let(:model) { ActiveModelProductFull }
21
+
22
+ before :all do
23
+ model.setup_index
24
+ end
25
+
26
+ it 'have :full mode' do
27
+ model.ac_opts[:mode].should == :full
28
+ end
29
+
30
+ it 'suggest for beginning of the source' do
31
+ model.ac_search('A.31').to_a.should_not be_empty
32
+ end
33
+
34
+ it 'suggest for for full match' do
35
+ model.ac_search('SAMARA').to_a.should_not be_empty
36
+ end
37
+
38
+ it 'don\'t suggest for unmatched term' do
39
+ model.ac_search('kac3').to_a.should be_empty
40
+ end
41
+
42
+ it 'suggest from the middle of the word' do
43
+ model.ac_search('/sm').to_a.should_not be_empty
44
+ end
45
+
46
+ it 'suggest with relevance order' do
47
+ model.ac_search('A.3').map(&:sku).should == ['A.3103', 'b A.3611']
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ class ActiveModelProductLocalized < StubModelBase
4
+ ac_field :name, :localized => true
5
+
6
+ def self.test_data
7
+ [
8
+ {:name_ru => 'name_ru first', :name_en => 'name_en first'},
9
+ {:name_ru => 'name_ru second', :name_en => 'name_en second'}
10
+ ]
11
+ end
12
+
13
+ def self.populate
14
+ test_data.each_with_index do |data, id|
15
+ u = new(data)
16
+ u.id = id
17
+ u.save
18
+ end
19
+ end
20
+ end
21
+
22
+ describe 'suggestions for localized attributes' do
23
+ let(:model) { ActiveModelProductLocalized }
24
+
25
+ before :all do
26
+ model.setup_index
27
+ end
28
+
29
+ it 'don\'t suggest from all locales' do
30
+ model.ac_search('name_en first').to_a.should have(1).results
31
+ model.ac_search('name_ru first').to_a.should have(1).results
32
+ model.ac_search('name_ru').to_a.should have(2).results
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ class ActiveModelUserPhrase < StubModelBase
4
+ ac_field :full_name, :mode => :phrase
5
+ end
6
+
7
+ describe ':phrase mode autocomplete' do
8
+ let(:model) { ActiveModelUserPhrase }
9
+ before :all do
10
+ model.setup_index
11
+ end
12
+
13
+ it 'have :phrase mode' do
14
+ model.ac_opts[:mode].should == :phrase
15
+ end
16
+
17
+ it_behaves_like 'basic autocomplete', ActiveModelUserPhrase
18
+
19
+ it 'don\'t suggest from the middle of the word' do
20
+ model.ac_search('becca').to_a.should be_empty
21
+ end
22
+
23
+ it 'don\'t for each word of the source' do
24
+ model.ac_search('Flores').map(&:full_name).should be_empty
25
+ end
26
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ class ActiveModelUserFilter < StubModelBase
4
+ ac_field :full_name
5
+
6
+ def self.test_data
7
+ [
8
+ {:full_name => 'Laura Nelson', :interest_ids => [1, 2]},
9
+ {:full_name => 'Laura Flores', :interest_ids => [2, 3]},
10
+ {:full_name => 'Laura Larson', :interest_ids => [3, 4]}
11
+ ]
12
+ end
13
+
14
+ def self.populate
15
+ test_data.each_with_index do |data, id|
16
+ u = new(data)
17
+ u.id = id
18
+ u.save
19
+ end
20
+ end
21
+
22
+ def to_indexed_json
23
+ for_json = {}
24
+ attrs = [:id, :created_at, :interest_ids] + self.class.ac_search_attrs
25
+ attrs.each do |attr|
26
+ for_json[attr] = send(attr)
27
+ end
28
+ MultiJson.encode(for_json)
29
+ end
30
+ end
31
+
32
+ describe 'search filters' do
33
+ let(:model) { ActiveModelUserFilter }
34
+
35
+ before :all do
36
+ model.setup_index
37
+ end
38
+
39
+ it 'filter suggestions with terms' do
40
+ model.ac_search('Laura', :with => {:interest_ids => [2]}).map(&:full_name).should =~ ['Laura Nelson', 'Laura Flores']
41
+ end
42
+
43
+ it 'accept coma separated string for filter' do
44
+ model.ac_search('Laura', :with => {:interest_ids => '1,4'}).map(&:full_name).should =~ ['Laura Nelson', 'Laura Larson']
45
+ end
46
+
47
+ it 'filter suggestions without terms' do
48
+ model.ac_search('Laura', :without => {:interest_ids => [2]}).map(&:full_name).should =~ ['Laura Larson']
49
+ end
50
+
51
+ it 'can order suggestions desc' do
52
+ res = model.ac_search('Laura', :order => :id, :sort_mode => 'desc').map(&:id)
53
+ res.should == res.sort.reverse
54
+ end
55
+
56
+ it 'can order suggestions asc' do
57
+ res = model.ac_search('Laura', :order => :id, :sort_mode => 'asc').map(&:id)
58
+ res.should == res.sort
59
+ end
60
+
61
+ it 'limit suggestions collection size' do
62
+ model.ac_search('Laura', :per_page => 1).to_a.should have(1).result
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ class ActiveModelUserWord < StubModelBase
4
+ ac_field :full_name, :mode => :word
5
+ end
6
+
7
+ describe ':word mode autocomplete' do
8
+ let(:model) { ActiveModelUserWord }
9
+
10
+ before :all do
11
+ model.setup_index
12
+ end
13
+
14
+ it 'have :word mode' do
15
+ model.ac_opts[:mode].should == :word
16
+ end
17
+
18
+ it_behaves_like 'basic autocomplete', ActiveModelUserWord
19
+
20
+ it 'don\'t suggest from the middle of the word' do
21
+ model.ac_search('becca').to_a.should be_empty
22
+ end
23
+
24
+ it 'suggest for each word of the source' do
25
+ model.ac_search('Flores').map(&:full_name).should == ['Joyce Flores']
26
+ end
27
+
28
+ it 'suggest with relevance order' do
29
+ model.ac_search('Lau').map(&:full_name).should == ['Laura Larson', 'Larson Laura']
30
+ end
31
+ end
@@ -0,0 +1,76 @@
1
+ require 'active_model'
2
+ require 'tire'
3
+ require 'active_support/core_ext'
4
+ require 'elasticsearch_autocomplete'
5
+
6
+ Tire.configure do
7
+ logger 'tmp/elasticsearch.log'
8
+ url 'http://localhost:9200'
9
+ pretty 1
10
+ end
11
+
12
+ I18n.available_locales = [:en, :ru]
13
+
14
+ class ActiveModelBase
15
+ include ActiveModel::AttributeMethods
16
+ include ActiveModel::Serialization
17
+ include ActiveModel::Serializers::JSON
18
+ include ActiveModel::Naming
19
+
20
+ extend ActiveModel::Callbacks
21
+ define_model_callbacks :save, :destroy
22
+
23
+ include ElasticsearchAutocomplete::ModelAddition
24
+
25
+ attr_reader :attributes
26
+ attr_accessor :id, :created_at
27
+
28
+ def initialize(attributes = {})
29
+ @attributes = attributes
30
+ end
31
+
32
+ def method_missing(id, *args, &block)
33
+ attributes[id.to_sym] || attributes[id.to_s] || super
34
+ end
35
+
36
+ def persisted?
37
+ true
38
+ end
39
+
40
+ def save
41
+ _run_save_callbacks do
42
+ end
43
+ end
44
+
45
+ def destroy
46
+ _run_destroy_callbacks do
47
+ @destroyed = true
48
+ end
49
+ end
50
+
51
+ def destroyed?
52
+ !!@destroyed
53
+ end
54
+
55
+ def self.setup_index
56
+ tire.index.delete
57
+ tire.create_elasticsearch_index
58
+ populate
59
+ tire.index.refresh
60
+ end
61
+ end
62
+
63
+ class StubModelBase < ActiveModelBase
64
+
65
+ def self.test_data
66
+ ['Joyce Flores', 'Rebecca Nelson', 'Larson Laura', 'Laura Larson']
67
+ end
68
+
69
+ def self.populate
70
+ test_data.each_with_index do |name, id|
71
+ u = new(:full_name => name)
72
+ u.id = id
73
+ u.save
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elasticsearch_autocomplete
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Alex Leschenko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ none: false
21
+ prerelease: false
22
+ name: tire
23
+ requirement: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ! '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ none: false
29
+ type: :runtime
30
+ - !ruby/object:Gem::Dependency
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ none: false
37
+ prerelease: false
38
+ name: rake
39
+ requirement: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ none: false
45
+ type: :development
46
+ - !ruby/object:Gem::Dependency
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ none: false
53
+ prerelease: false
54
+ name: rspec
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ none: false
61
+ type: :development
62
+ - !ruby/object:Gem::Dependency
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ none: false
69
+ prerelease: false
70
+ name: oj
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ none: false
77
+ type: :development
78
+ description: Simple autocomplete for your models using awesome elasticsearch and tire
79
+ gem
80
+ email:
81
+ - leschenko.al@gmail.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - elasticsearch_autocomplete.gemspec
92
+ - lib/elasticsearch_autocomplete.rb
93
+ - lib/elasticsearch_autocomplete/analyzers.rb
94
+ - lib/elasticsearch_autocomplete/model_addition.rb
95
+ - lib/elasticsearch_autocomplete/railtie.rb
96
+ - lib/elasticsearch_autocomplete/version.rb
97
+ - spec/elasticsearch_autocomplete/base_spec.rb
98
+ - spec/elasticsearch_autocomplete/full_mode_spec.rb
99
+ - spec/elasticsearch_autocomplete/localized_spec.rb
100
+ - spec/elasticsearch_autocomplete/phrase_mode_spec.rb
101
+ - spec/elasticsearch_autocomplete/search_filters_spec.rb
102
+ - spec/elasticsearch_autocomplete/word_mode_spec.rb
103
+ - spec/spec_helper.rb
104
+ homepage: https://github.com/leschenko/elasticsearch_autocomplete
105
+ licenses: []
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ segments:
116
+ - 0
117
+ hash: 4533165797483734826
118
+ none: false
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ segments:
125
+ - 0
126
+ hash: 4533165797483734826
127
+ none: false
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 1.8.24
131
+ signing_key:
132
+ specification_version: 3
133
+ summary: Elasticsearch autocomplete for models
134
+ test_files:
135
+ - spec/elasticsearch_autocomplete/base_spec.rb
136
+ - spec/elasticsearch_autocomplete/full_mode_spec.rb
137
+ - spec/elasticsearch_autocomplete/localized_spec.rb
138
+ - spec/elasticsearch_autocomplete/phrase_mode_spec.rb
139
+ - spec/elasticsearch_autocomplete/search_filters_spec.rb
140
+ - spec/elasticsearch_autocomplete/word_mode_spec.rb
141
+ - spec/spec_helper.rb