elasticsearch-autocomplete 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: 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: []