elasticsearch-autocomplete 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: 3a632d2806037ffcbd72284c1740ffbb74e62583
4
+ data.tar.gz: a5886398ab278a66e506b05bcf4909352de8b745
5
+ SHA512:
6
+ metadata.gz: 070583334041cff1cc3a18d207627fe25356769dcec49a885ec427b909ec7a44452f49ec0315bdb78d0c8d90a34b9810728f1e691055dd37cd13659a108b2f3c
7
+ data.tar.gz: 6490e715fe8d4712aed10d1586bc90f2b8b11b0543db858ffa16e7d24892505aef1836f9ff811f20463631ecdc4c0c1726c70177a9374d3de4f081a81d45001e
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ## 0.1.1
2
+
3
+ - Added {field}.beginning to filters
4
+ - Added analysis filter `ngram_beginning` for analyser `beginning`
5
+ - Updated specs and README to reflect addition
6
+ - Current fields for `multi_field`
7
+ - field, field.exact, field.beginning, field.autocomplete, field.fuzzy
8
+
9
+ ## 0.1.0
10
+
11
+ - Initial Release
data/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # ElasticSearch Autocomplete
2
+ [![Code Climate](https://codeclimate.com/github/spodlecki/elasticsearch-autocomplete/badges/gpa.svg)](https://codeclimate.com/github/spodlecki/elasticsearch-autocomplete)
3
+ [![Test Coverage](https://codeclimate.com/github/spodlecki/elasticsearch-autocomplete/badges/coverage.svg)](https://codeclimate.com/github/spodlecki/elasticsearch-autocomplete/coverage)
4
+ [![Build Status](https://travis-ci.org/spodlecki/elasticsearch-autocomplete.svg)](https://travis-ci.org/spodlecki/elasticsearch-autocomplete)
5
+
6
+ Quick and simple way to perform autocomplete with ElasticSearch Servers.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```
13
+ gem 'elasticsearch-autocomplete'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ ## Dependencies
21
+
22
+ - [Elasticsearch::Model](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-model)
23
+ - [Rails > 3.2.8](http://rubyonrails.org/)
24
+ - [Elasticsearch Server > 1.0.1](http://www.elastic.co)
25
+
26
+ ## Usage
27
+
28
+ ### Field Explaination
29
+
30
+ ![Quick Brown Fox](http://i57.tinypic.com/vdivie.png)
31
+ Access each field as if they were a nested attribute (`field.fuzzy`, `field.exact`, ...).
32
+
33
+ ### Searching
34
+
35
+ **map file**
36
+ (**ROOT**/lib/elasticsearch/autocomplete/**TYPE**_type.rb)
37
+
38
+ ```
39
+ module Elasticsearch
40
+ module Autocomplete
41
+ class ColorType < Type
42
+ # Value of ElasticSearch's _type
43
+ # => String
44
+ def type
45
+ 'color'
46
+ end
47
+
48
+ # Search Hash
49
+ # This is the entire search query for ElasticSearch
50
+ #
51
+ def to_hash
52
+ {
53
+ query: { match_all: {}},
54
+ filter: {
55
+ and: [
56
+ { term: {_type: 'color'} }
57
+ ]
58
+ }
59
+ }
60
+ end
61
+
62
+ # Return the hash to which the result is mapped from
63
+ # @method mapped
64
+ # @param source {OpenStruct} Source value from ElasticSearch result
65
+ # @return Hash
66
+ #
67
+ def mapped(source)
68
+ {
69
+ id: source.id,
70
+ name: source.name,
71
+ image: image_path(source.icon),
72
+ type: type
73
+ }
74
+ end
75
+
76
+ # Optional to set
77
+ # If you intend on using any of the helpers, this will need to be set
78
+ #
79
+ def field
80
+ :name
81
+ end
82
+ end
83
+ end
84
+ end
85
+ ```
86
+
87
+ **controller**
88
+ ```
89
+ # Search Multiple Types, each with their own query
90
+ search = Elasticsearch::Autocomplete::Search.find(index_string, params[:term], ['actress'])
91
+ search.results # => []
92
+
93
+ # Multiple Types, with a single result set
94
+ # @param index {String} ElasticSearch Index Name
95
+ # @param term {String} Query String being searched
96
+ # @param types {Array} Array of types being searched for in ElasticSearch
97
+ # @param multisearch {Boolean} Are we splitting each type into its own query?
98
+ # @param field {String|Symbol} If doing a single query, pass the name of the field to be searched
99
+ search = Elasticsearch::Autocomplete::Search.find(index_string, params[:term], ['actress'], false, :name)
100
+ search.results # => []
101
+ ```
102
+
103
+ **model indexing**
104
+ ```
105
+ # Assuming you are using Elasticsearch::Model as your indexing tool
106
+
107
+ mapping do
108
+ # ...
109
+ indexes :field_name, Elasticsearch::Autocomplete::Filters.config(:field_name)
110
+ # ...
111
+ end
112
+ ```
113
+
114
+ ## Development
115
+
116
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
117
+
118
+ ## Type Helper Methods
119
+
120
+ ```
121
+ def image_path(image_string)
122
+ # => Generates an asset url for an image
123
+ end
124
+ ```
125
+
126
+ ```
127
+ # https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html
128
+ # Quickly use the multiple fields matcher search format
129
+ # Usage: { query: multi_match({exact: 30}) }
130
+ #
131
+ def multi_match(rank_overrides={})
132
+ ranks = {exact: 10, beginning: 7, autocomplete: 5, fuzzy: 1}.merge(rank_overrides)
133
+
134
+ {..}
135
+ # => returns built hash for the multi_match query
136
+ end
137
+ ```
138
+
139
+ ## Contributing
140
+
141
+ 1. Fork it
142
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
143
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
144
+ 4. Push to the branch (`git push origin my-new-feature`)
145
+ 5. Create new Pull Request
146
+
147
+ ## TODO
148
+
149
+ - Create generator for new types
150
+ - Get codeclimate online
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ Bundler::GemHelper.install_tasks
6
+
7
+
8
+ task :default => :spec
@@ -0,0 +1 @@
1
+ require 'elasticsearch/autocomplete'
@@ -0,0 +1,16 @@
1
+ require 'elasticsearch/autocomplete/version'
2
+
3
+ require 'elasticsearch/autocomplete/filters'
4
+ require 'elasticsearch/autocomplete/type'
5
+ require 'elasticsearch/autocomplete/request'
6
+ require 'elasticsearch/autocomplete/multiple_request'
7
+ require 'elasticsearch/autocomplete/single_request'
8
+ require 'elasticsearch/autocomplete/response'
9
+ require 'elasticsearch/autocomplete/multiple_response'
10
+ require 'elasticsearch/autocomplete/single_response'
11
+ require 'elasticsearch/autocomplete/search'
12
+
13
+ module Elasticsearch
14
+ module Autocomplete
15
+ end
16
+ end
@@ -0,0 +1,72 @@
1
+ # TODO
2
+ # Add beginning of entire string rank
3
+
4
+ module Elasticsearch
5
+ module Autocomplete
6
+ module Filters
7
+ module ClassMethods
8
+ def analysis
9
+ {
10
+ analysis: {
11
+ analyzer: {
12
+ simple: {
13
+ type: "custom",
14
+ tokenizer: "standard",
15
+ filter: [ "standard", "lowercase", "stop" ]
16
+ },
17
+ autocomplete: {
18
+ type: "custom",
19
+ tokenizer: "standard",
20
+ filter: [ 'asciifolding', "standard", "lowercase", "stop", "kstem", "ngram_front" ]
21
+ },
22
+ fuzzy: {
23
+ type: "custom",
24
+ tokenizer: "standard",
25
+ filter: [ 'asciifolding', "standard", "lowercase", "stop", "kstem", "ngram_short" ]
26
+ },
27
+ beginning: {
28
+ type: "custom",
29
+ tokenizer: "keyword",
30
+ filter: [ "lowercase", 'asciifolding', 'ngram_beginning' ]
31
+ }
32
+ },
33
+ filter: {
34
+ ngram_beginning: {
35
+ type: "edgeNGram",
36
+ min_gram: 2,
37
+ max_gram: 30
38
+ },
39
+ ngram_front: {
40
+ type: "edgeNGram",
41
+ min_gram: 2,
42
+ max_gram: 15
43
+ },
44
+ ngram_short: {
45
+ type: "ngram",
46
+ min_gram: 2,
47
+ max_gram: 15
48
+ }
49
+ }
50
+ }
51
+ }
52
+ end
53
+
54
+ def build_config(field)
55
+ {
56
+ :type => :multi_field,
57
+ :fields => {
58
+ field.to_sym => { type: 'string' },
59
+ :"#{field}.exact" => { type: 'string', index_analyzer: 'simple' },
60
+ :"#{field}.beginning" => { type: 'string', index_analyzer: 'beginning'},
61
+ :"#{field}.autocomplete" => { type: 'string', index_analyzer: 'autocomplete'},
62
+ :"#{field}.fuzzy" => { type: 'string', index_analyzer: 'fuzzy'}
63
+ }
64
+ }
65
+ end
66
+
67
+ alias_method :config, :build_config
68
+ end
69
+ extend ClassMethods
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,36 @@
1
+ ###
2
+ # Returns the search body as a hash for autocomplete module
3
+ #
4
+ # Building the hash in a specific format is vital to developing
5
+ # a multi-query search from elasticsearch. Results are returned in the specific
6
+ # order you submit the query in.
7
+ #
8
+ # Each 'type' should have a corrisponding class.
9
+ # Example:
10
+ # If the type would be a 'tag'-
11
+ # => Elasticsearch::Autocomplete::TagType
12
+ ###
13
+
14
+ module Elasticsearch
15
+ module Autocomplete
16
+ class MultipleRequest < Request
17
+ def body
18
+ @body ||= begin
19
+ result = []
20
+ types.each do |t|
21
+ result << index_hash
22
+ result << t.to_hash
23
+ end
24
+
25
+ result
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def index_hash
32
+ {:index => index}
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,6 @@
1
+ module Elasticsearch
2
+ module Autocomplete
3
+ class MultipleResponse < Response
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,30 @@
1
+ ###
2
+ # Returns the search body as a hash for autocomplete module
3
+ #
4
+ # Building the hash in a specific format is vital to developing
5
+ # a multi-query search from elasticsearch. Results are returned in the specific
6
+ # order you submit the query in.
7
+ #
8
+ # Each 'type' should have a corrisponding class.
9
+ # Example:
10
+ # If the type would be a 'tag'-
11
+ # => Elasticsearch::Autocomplete::TagType
12
+ ###
13
+
14
+ module Elasticsearch
15
+ module Autocomplete
16
+ class Request
17
+ attr_accessor :query, :types, :index
18
+
19
+ def initialize(index, query, types)
20
+ self.query = query
21
+ self.types = types
22
+ self.index = index
23
+ end
24
+
25
+ def body
26
+ {}
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,53 @@
1
+ require 'ostruct'
2
+ require 'elasticsearch/model'
3
+
4
+ module Elasticsearch
5
+ module Autocomplete
6
+ class Response
7
+ include Enumerable
8
+
9
+ delegate :body, to: :request
10
+
11
+ attr_accessor :types, :request, :results
12
+
13
+ def initialize(types, request)
14
+ self.request = request
15
+ self.types = types
16
+ self.results = search
17
+ end
18
+
19
+ def each(&block)
20
+ results.each do |result|
21
+ member = parse_response(result)
22
+
23
+ block.call(member)
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def search
30
+ r = Elasticsearch::Model.client.msearch(:body => body)
31
+ r['responses']
32
+ end
33
+
34
+ def parse_response(results)
35
+ results['hits']['hits'].map do |search_result|
36
+ src = search_result['_source']
37
+ type = search_result['_type']
38
+ score = search_result['_score']
39
+ returned_type = types.select{|t| t.type == type }.first
40
+
41
+ if returned_type
42
+ returned_type.mapped(
43
+ OpenStruct.new(src)
44
+ )
45
+ else
46
+ src
47
+ end.merge(:_score => score, :_type => type)
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,57 @@
1
+ module Elasticsearch
2
+ module Autocomplete
3
+ class Search
4
+ module ClassMethods
5
+ def find(index, query, types=[])
6
+ search = self.new(index, query, types)
7
+ end
8
+ end
9
+ extend ClassMethods
10
+
11
+ attr_accessor :query, :types, :index, :multisearch, :field
12
+
13
+ def initialize(index, query, types, multisearch=true, field=nil)
14
+ raise "Specify a field when running a single search." if multisearch == false && field.nil?
15
+
16
+ self.query = query
17
+ self.types = types_mapped(types)
18
+ self.index = index
19
+
20
+ self.multisearch = multisearch
21
+ self.field = field
22
+ end
23
+
24
+ def results
25
+ @results ||= begin
26
+ if multisearch
27
+ MultipleResponse.new(types, request)
28
+ else
29
+ SingleResponse.new(types, request)
30
+ end
31
+ end
32
+ end
33
+
34
+ def request
35
+ @request ||= begin
36
+ if multisearch
37
+ MultipleRequest.new(index, query, types)
38
+ else
39
+ SingleRequest.new(index, query, types, field)
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def types_mapped(types)
47
+ types = Array(types)
48
+
49
+ types.map do |type|
50
+
51
+ "Elasticsearch::Autocomplete::#{type.capitalize}Type".constantize
52
+ .new(query)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,37 @@
1
+ ###
2
+ # Returns the search body as a hash for autocomplete module
3
+ #
4
+ # Building the hash in a specific format is vital to developing
5
+ # a multi-query search from elasticsearch. Results are returned in the specific
6
+ # order you submit the query in.
7
+ #
8
+ # Each 'type' should have a corrisponding class.
9
+ # Example:
10
+ # If the type would be a 'tag'-
11
+ # => Elasticsearch::Autocomplete::TagType
12
+ ###
13
+
14
+ module Elasticsearch
15
+ module Autocomplete
16
+ class SingleRequest < Request
17
+ attr_accessor :field
18
+
19
+ def initialize(index, query, types, field)
20
+ super(index, query, types)
21
+ self.field = field
22
+ end
23
+
24
+ def body
25
+ @body ||= begin
26
+ {
27
+ :index => index,
28
+ :type => types.map(&:type).uniq,
29
+ :body => {
30
+ :query => Type.single_search(query, field)
31
+ }
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ module Elasticsearch
2
+ module Autocomplete
3
+ class SingleResponse < Response
4
+
5
+ def each(&block)
6
+ members = parse_response(results)
7
+ members.each do |member|
8
+ block.call(member)
9
+ end
10
+ end
11
+
12
+ protected
13
+
14
+ def search
15
+ @search ||= Elasticsearch::Model.client.search(body)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,74 @@
1
+ require 'action_controller'
2
+
3
+ module Elasticsearch
4
+ module Autocomplete
5
+ class Type
6
+
7
+ module ClassMethods
8
+ def single_search(query, field)
9
+ t = self.new(query)
10
+ t.send(:multi_match, {}, query, field)
11
+ end
12
+ end
13
+
14
+ extend ClassMethods
15
+
16
+ attr_accessor :query
17
+
18
+ def initialize(query)
19
+ self.query = query
20
+ end
21
+
22
+ # => String
23
+ def type
24
+ raise "Replace #type with the ElasticSearch _type"
25
+ end
26
+
27
+ # => Hash
28
+ def to_hash
29
+ raise "Replace #to_hash with ElasticSearch Query Hash"
30
+ end
31
+
32
+ # Return the hash to which the result is mapped from
33
+ # @param source {OpenStruct} Source value from ElasticSearch result
34
+ # => Hash
35
+ def mapped(source)
36
+ raise "Replace #mapped with a mapped response hash"
37
+ end
38
+
39
+ # The field to search on
40
+ # You'll only need this if you intend on using the search
41
+ # helpers below
42
+ #
43
+ def field
44
+ raise "Replace #field with the ElasticSearch field name"
45
+ end
46
+
47
+ protected
48
+
49
+ def image_path(img)
50
+ return if img.blank?
51
+ ActionController::Base.helpers.image_path(img)
52
+ end
53
+
54
+ # https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html
55
+ # Quickly use the multiple fields matcher search format
56
+ # => { query: multi_match({exact: 30}) }
57
+ #
58
+ def multi_match(rank_overrides={}, query_override=nil, field_override=nil, tie_breaker=0.3)
59
+ ranks = {exact: 10, beginning: 7, autocomplete: 5, fuzzy: 1}.merge(rank_overrides)
60
+ field_override ||= field
61
+ query_override ||= query
62
+
63
+ {
64
+ :multi_match => {
65
+ :query => query_override,
66
+ :type => 'most_fields',
67
+ :fields => ranks.map{|k,v| :"#{field_override}.#{k}^#{v}" },
68
+ :tie_breaker => tie_breaker
69
+ }
70
+ }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ module Elasticsearch
2
+ module Autocomplete
3
+ VERSION = "0.1.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elasticsearch-autocomplete
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - s.podlecki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: elasticsearch
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: elasticsearch-model
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: elasticsearch-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
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
+ description: Include autocomplete into your project. Base capability can perform multiple
112
+ queries per request.
113
+ email:
114
+ - s.podlecki@gmail.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - CHANGELOG.md
120
+ - README.md
121
+ - Rakefile
122
+ - lib/elasticsearch.rb
123
+ - lib/elasticsearch/autocomplete.rb
124
+ - lib/elasticsearch/autocomplete/filters.rb
125
+ - lib/elasticsearch/autocomplete/multiple_request.rb
126
+ - lib/elasticsearch/autocomplete/multiple_response.rb
127
+ - lib/elasticsearch/autocomplete/request.rb
128
+ - lib/elasticsearch/autocomplete/response.rb
129
+ - lib/elasticsearch/autocomplete/search.rb
130
+ - lib/elasticsearch/autocomplete/single_request.rb
131
+ - lib/elasticsearch/autocomplete/single_response.rb
132
+ - lib/elasticsearch/autocomplete/type.rb
133
+ - lib/elasticsearch/autocomplete/version.rb
134
+ homepage: https://github.com/spodlecki/elasticsearch-autocomplete
135
+ licenses: []
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 2.4.6
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Autocomplete Helpers and Builder
157
+ test_files: []