azure_search 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bc148b6807b2928ac0842c5f0839cad1375fa433
4
+ data.tar.gz: b696a4ceb1c8a277e5bd9639b8fc326ffbfd32cf
5
+ SHA512:
6
+ metadata.gz: 7f8441c96cae85be15a736906bb1a31c489e201fb20318bdb09107369494ef9bf55d9f23fc657ee97e083b79f5cd921cf5a049fa92c5dbdb4656d2d8b8623a52
7
+ data.tar.gz: 03f9bfa8c10d7c189620dc21928c995613426f89f6d126d757e27d94c61b7efdeb77b4502e4e13d58786b6be94ca9b5361414bc4d452a0ffbe6535eb845a8942
@@ -0,0 +1,7 @@
1
+ require 'azure_search/utils'
2
+ require 'azure_search/errors'
3
+ require 'azure_search/version'
4
+ require 'azure_search/index_field'
5
+ require 'azure_search/search_index_client'
6
+ require 'azure_search/index_search_options'
7
+ require 'azure_search/index_batch_operation'
@@ -0,0 +1,26 @@
1
+ module AzureSearch
2
+
3
+ # Defines errors that are raised by AzureSearch client.
4
+ #
5
+ # @since 0.1.0
6
+ module Errors
7
+
8
+ # Raised to indicate that a HTTP request needs to be retried.
9
+ class RetriableHttpError < StandardError
10
+ # @return [Symbol] The response status code.
11
+ attr_accessor :code
12
+ # @return [Symbol] The error message.
13
+ attr_accessor :message
14
+
15
+ def initialize(code, message)
16
+ self.code = code
17
+ self.message = message
18
+ end
19
+
20
+ def to_s
21
+ "RetriableHttpError <#{self.code}>: #{self.message}"
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,59 @@
1
+ require 'json'
2
+
3
+ module AzureSearch
4
+
5
+ # Represents a batch operation in an indexing request.
6
+ class IndexBatchOperation
7
+ # Encapsulates the supplied document in an IndexUploadOperation.
8
+ #
9
+ # @param [Hash] document The document.
10
+ # @return [Hash] The upload operation as a Hash.
11
+ def self.upload(document)
12
+ IndexUploadOperation.new(document).to_hash
13
+ end
14
+
15
+ # Encapsulates a delete request in an IndexDeleteOperation.
16
+ #
17
+ # @param [String] key_name The key name.
18
+ # @param [String] key_value The key value.
19
+ def self.delete(key_name, key_value)
20
+ IndexDeleteOperation.new(key_name, key_value).to_hash
21
+ end
22
+
23
+ # @todo Add support for merge & merge_or_upload
24
+ end
25
+
26
+ # Represents an upload operation of a document.
27
+ class IndexUploadOperation < IndexBatchOperation
28
+ def initialize(document)
29
+ @document = document
30
+ end
31
+
32
+ # Returns the upload operation as a Hash.
33
+ #
34
+ # @return [Hash] The upload operation.
35
+ def to_hash
36
+ @document["@search.action"] = "upload"
37
+ @document
38
+ end
39
+ end
40
+
41
+ # Represents a document deletion request.
42
+ class IndexDeleteOperation < IndexBatchOperation
43
+ def initialize(key_name, key_value)
44
+ @key_name = key_name
45
+ @key_value = key_value
46
+ end
47
+
48
+ # Returns the delete operation as a Hash.
49
+ #
50
+ # @return [Hash] The delete operation.
51
+ def to_hash
52
+ {
53
+ @key_name => @key_value,
54
+ "@search_action" => "delete"
55
+ }
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,104 @@
1
+ module AzureSearch
2
+
3
+ # Represents an Index field definition.
4
+ class IndexField
5
+ # Valid EDM (Entity Data Model) data types.
6
+ VALID_EDM_TYPES = [
7
+ "Edm.String",
8
+ "Collection(Edm.String)",
9
+ "Edm.Boolean",
10
+ "Edm.Int32",
11
+ "Edm.Int64",
12
+ "Edm.Double",
13
+ "Edm.DateTimeOffset",
14
+ "Edm.GeographyPoint"
15
+ ].freeze
16
+
17
+ def initialize(name, type)
18
+ raise "Field name is required." unless !name.empty?
19
+ @name = name
20
+ raise "Invalid field type." unless VALID_EDM_TYPES.include? type
21
+ @type = type
22
+ end
23
+
24
+ # Sets the analyzer name used for search and indexing.
25
+ #
26
+ # @param [String] analyzer The analyzer name.
27
+ # @return [self] The current instance.
28
+ def analyzer(analyzer)
29
+ @analyzer = analyzer
30
+ self
31
+ end
32
+
33
+ # Sets the field to be searchable.
34
+ #
35
+ # @param [TrueClass,FalseClass] searchable true or false.
36
+ # @return [self] The current instance.
37
+ def searchable(searchable)
38
+ raise "searchable must be a boolean." unless is_bool? searchable
39
+ @searchable = searchable
40
+ self
41
+ end
42
+
43
+ # Sets the field to be filterable.
44
+ #
45
+ # @param [TrueClass,FalseClass] filterable true or false.
46
+ # @return [self] The current instance.
47
+ def filterable(filterable)
48
+ raise "filterable must be a boolean." unless is_bool? filterable
49
+ @filterable = filterable
50
+ self
51
+ end
52
+
53
+ # Sets the field to be retrievable.
54
+ #
55
+ # @param [TrueClass,FalseClass] retrievable true or false.
56
+ # @return [self] The current instance.
57
+ def retrievable(retrievable)
58
+ raise "retrievable must be a boolean." unless is_bool? retrievable
59
+ @retrievable = retrievable
60
+ self
61
+ end
62
+
63
+ # Sets the field to be sortable.
64
+ #
65
+ # @param [TrueClass,FalseClass] sortable true or false.
66
+ # @return [self] The current instance.
67
+ def sortable(sortable)
68
+ raise "sortable must be a boolean." unless is_bool? sortable
69
+ @sortable = sortable
70
+ self
71
+ end
72
+
73
+ # Sets the field to be facetable.
74
+ #
75
+ # @param [TrueClass,FalseClass] facetable true or false.
76
+ # @return [self] The current instance.
77
+ def facetable(facetable)
78
+ raise "facetable must be a boolean." unless is_bool? facetable
79
+ @facetable = facetable
80
+ self
81
+ end
82
+
83
+ # Sets the field to be the key (Only Edm.String fields can be keys).
84
+ #
85
+ # @param [TrueClass,FalseClass] key true or false.
86
+ # @return [self] The current instance.
87
+ def key(key)
88
+ raise "key must be a boolean." unless is_bool? key
89
+ raise "Only Edm.String fields can be keys." unless @type == "Edm.String"
90
+ @key = key
91
+ self
92
+ end
93
+
94
+ # Converts this IndexField to a Hash.
95
+ #
96
+ # @return [Hash] Hash representation of IndexField.
97
+ def to_hash
98
+ hash = {}
99
+ instance_variables.each {|var| hash[var.to_s.delete("@")] = instance_variable_get(var) }
100
+ hash
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,177 @@
1
+ require 'azure_search/utils'
2
+
3
+ module AzureSearch
4
+
5
+ # Represents search options that will be sent along with a search request.
6
+ class IndexSearchOptions
7
+ SPECIAL_PARAMS = %w(count filter orderby select top skip).freeze
8
+
9
+ # Specifies whether to fetch the total count of results.
10
+ #
11
+ # @param [TrueClass,FalseClass] val true or false.
12
+ # @return [self] the current instance.
13
+ def include_count(val)
14
+ raise "include_count requires a boolean value." unless is_bool?(val)
15
+ @count = val
16
+ self
17
+ end
18
+
19
+ # Sets the structured search expression in standard OData syntax.
20
+ #
21
+ # @param [String] val The filter expression.
22
+ # @return [self] The current instance.
23
+ def filter(val)
24
+ raise "filter requires a String." unless val.is_a? String
25
+ @filter = val
26
+ self
27
+ end
28
+
29
+ # Sets the list of comma-separated expressions to sort the results by.
30
+ #
31
+ # @param [String] val Comma-separated expressions.
32
+ # @return [self] The current instance.
33
+ def order_by(val)
34
+ raise "order_by requires a String." unless val.is_a? String
35
+ @orderby = val
36
+ self
37
+ end
38
+
39
+ # Sets the list of comma-separated fields to retrieve.
40
+ #
41
+ # @param [String] val Comma-separated fields.
42
+ # @return [self] The current instance.
43
+ def select(val)
44
+ raise "select requires a String." unless val.is_a? String
45
+ @select = val
46
+ self
47
+ end
48
+
49
+ # Sets the list of comma-separated field names to search for the specified text.
50
+ #
51
+ # @param [String] val Comma-separated field names.
52
+ # @return [self] The current instance.
53
+ def search_fields(val)
54
+ raise "search_fields requires a String." unless val.is_a? String
55
+ @searchFields = val
56
+ self
57
+ end
58
+
59
+ # Sets the list of fields to facet by.
60
+ #
61
+ # @param [Array] fs The list of fields.
62
+ # @return [self] The current instance.
63
+ def facets(fs)
64
+ raise "facts requires an Array of Strings." unless fs.is_a? Array
65
+ @facet = fs
66
+ self
67
+ end
68
+
69
+ # Sets the set of comma-separated field names used for hit highlights.
70
+ #
71
+ # @param [String] hl Comma-separated field names.
72
+ # @return [self] The current instance.
73
+ def highlight(hl)
74
+ raise "highlight requires a String." unless hl.is_a? String
75
+ @highlight = hl
76
+ self
77
+ end
78
+
79
+ # Sets the string tag that appends to hit highlights (defaults to <em>).
80
+ #
81
+ # @param [String] hpt The string tag.
82
+ # @return [self] The current instance.
83
+ def highlight_pre_tag(hpt)
84
+ raise "highlight_pre_tag requires an HTML string." unless has_html?(hpt)
85
+ @highlightPreTag = hpt
86
+ self
87
+ end
88
+
89
+ # Sets the string tag that appends to hit highlights (defaults to </em>).
90
+ #
91
+ # @param [String] hpt The string tag.
92
+ # @return [self] The current instance.
93
+ def highlight_post_tag(hpt)
94
+ raise "highlight_post_tag requires an HTML string." unless has_html?(hpt)
95
+ @highlightPostTag = hpt
96
+ self
97
+ end
98
+
99
+ # Sets the name of a scoring profile to evaluate match scores for matching documents.
100
+ #
101
+ # @param [String] sp The scoring profile name.
102
+ # @return [self] The current instance.
103
+ def scoring_profile(sp)
104
+ raise "scoring_profile requires a String." unless sp.is_a? String
105
+ @scoringProfile = sp
106
+ self
107
+ end
108
+
109
+ # Sets the values for each parameter defined in a scoring function.
110
+ #
111
+ # @param [Array] params The parameters values.
112
+ # @return [self] The current instance.
113
+ def scoring_parameters(params)
114
+ raise "scoring_parameters requires an Array of Strings." unless params.is_a? Array
115
+ @scoringParameter = params
116
+ self
117
+ end
118
+
119
+ # Sets the number of search results to retrieve (defaults to 50).
120
+ #
121
+ # @param [Integer] val The number of search results.
122
+ # @return [self] The current instance.
123
+ def top(val)
124
+ raise "top requires an Integer." unless val.is_a? Integer
125
+ @top = val
126
+ self
127
+ end
128
+
129
+ # Sets the number of search results to skip.
130
+ #
131
+ # @param [Integer] val The number to skip (cannot be greater than 100,000).
132
+ # @return [self] The current instance.
133
+ def skip(val)
134
+ raise "skip requires an Integer." unless val.is_a? Integer
135
+ @skip = val
136
+ self
137
+ end
138
+
139
+ # Specifies whether any or all of the search terms must be matched.
140
+ #
141
+ # @param [String] mode The search mode.
142
+ # @return [self] The current instance.
143
+ def search_mode(mode)
144
+ raise "invalid search mode." unless ["any", "all"].include? mode
145
+ @searchMode = mode
146
+ self
147
+ end
148
+
149
+ # Sets the percentage of the index that must be covered by a search query.
150
+ #
151
+ # @param [Float] val The percentage (must be between 0 and 100).
152
+ # @return [self] The current instance.
153
+ def minimum_coverage(val)
154
+ raise "minimum_coverage requires a Float." unless val.is_a? Float
155
+ raise "minimum_coverage must be between 0 and 100" unless val.between?(0, 100)
156
+ @minimumCoverage = val
157
+ self
158
+ end
159
+
160
+ # Returns the search options as a Hash.
161
+ #
162
+ # @return [Hash] The search options as a Hash.
163
+ def to_hash
164
+ hash = {}
165
+ instance_variables.each {|var|
166
+ varname = var.to_s.delete("@")
167
+ if SPECIAL_PARAMS.include? varname
168
+ hash["$"+varname] = instance_variable_get(var)
169
+ else
170
+ hash[varname] = instance_variable_get(var)
171
+ end
172
+ }
173
+ hash
174
+ end
175
+ end
176
+
177
+ end
@@ -0,0 +1,169 @@
1
+ require 'azure_search/index_search_options'
2
+ require 'azure_search/errors'
3
+ require 'azure_search/utils'
4
+ require 'http'
5
+
6
+ # Core module common across the entire API.
7
+ #
8
+ # @author Faissal Elamraoui
9
+ # @since 0.1.0
10
+ module AzureSearch
11
+ API_VERSION = "2016-09-01".freeze
12
+
13
+ # Client to perform requests to an Azure Search service.
14
+ class SearchIndexClient
15
+ include AzureSearch::Errors
16
+
17
+ # @return [Symbol] The search service name.
18
+ attr_accessor :service_name
19
+ # @return [Symbol] The index name.
20
+ attr_accessor :index_name
21
+ # @return [Symbol] The API key generated for the provisioned Search service.
22
+ attr_accessor :api_key
23
+
24
+ def initialize(service_name, index_name, api_key)
25
+ if service_name.empty? || index_name.empty? || api_key.empty?
26
+ raise StandardError, "Must provide service_name, index_name and api_key when creating client."
27
+ end
28
+
29
+ self.service_name = service_name
30
+ self.index_name = index_name
31
+ self.api_key = api_key
32
+ end
33
+
34
+ # Checks if the specified index exists.
35
+ #
36
+ # @return [TrueClass,FalseClass] true if the index exists, false otherwise.
37
+ def exists
38
+ resp = create_request().get(build_index_definition_url())
39
+ if resp.code == 404
40
+ return false
41
+ end
42
+ raise_on_http_error(resp)
43
+ return true
44
+ end
45
+
46
+ # Creates the index based on the provided index definition.
47
+ #
48
+ # @param [Hash] definition The index definition.
49
+ # @option definition [String] :name The index name.
50
+ # @option definition [Array<IndexField>] :fields Array of fields.
51
+ # @option definition [Array] :suggesters Array of suggesters.
52
+ def create(definition)
53
+ raise "Index definition must be a Hash." unless definition.is_a? Hash
54
+ definition[:name] = self.index_name unless !definition[:name]
55
+ resp = create_request().post(build_index_list_url(), :json => definition)
56
+ raise_on_http_error(resp)
57
+ return JSON.parse(resp.to_s)
58
+ end
59
+
60
+ # Creates or updates the index based on the provided index definition.
61
+ #
62
+ # @param [Hash] definition The index definition.
63
+ # @option definition [String] :name The index name.
64
+ # @option definition [Array<IndexField>] :fields Array of fields.
65
+ # @option definition [Array] :suggesters Array of suggesters.
66
+ def create_or_update(definition)
67
+ raise "Index definition must be a Hash." unless definition.is_a? Hash
68
+ definition[:name] = self.index_name unless definition[:name]
69
+ resp = create_request().put(build_index_definition_url(), :json => definition)
70
+ raise_on_http_error(resp)
71
+ return resp.to_s.empty? ? nil : JSON.parse(resp.to_s)
72
+ end
73
+
74
+ # Deletes the specified index.
75
+ #
76
+ # @return [TrueClass,FalseClass] true if the index is deleted, false otherwise.
77
+ def delete
78
+ resp = create_request().delete(build_index_definition_url())
79
+ if resp.code == 404
80
+ return false
81
+ end
82
+ raise_on_http_error(resp)
83
+ return true
84
+ end
85
+
86
+ # Inserts documents in batch.
87
+ #
88
+ # @param [Array<IndexBatchOperation>] operations Array of upload operations.
89
+ # @param [Integer] chunk_size The batch size. Must not exceed 1000 documents.
90
+ def batch_insert(operations, chunk_size=1000)
91
+ raise "Batch request must not exceed 1000 documents." unless chunk_size <= 1000
92
+ operations.each_slice(chunk_size)
93
+ .to_a
94
+ .each{|op|
95
+ resp = create_request().post(build_indexing_url(), :json => {:value => op})
96
+ raise_on_http_error(resp)
97
+ resp.to_s
98
+ }
99
+ end
100
+
101
+ # Search the index for the supplied text.
102
+ #
103
+ # @param [String] text The text to search for.
104
+ # @param [IndexSearchOptions] options Options to configure the search request.
105
+ #
106
+ # @return [Hash] Parsed JSON response.
107
+ def search(text, options)
108
+ resp = create_request().get(build_index_search_url(text, options))
109
+ raise_on_http_error(resp)
110
+ return JSON.parse(resp.to_s)
111
+ end
112
+
113
+ # Lookup an indexed document by key.
114
+ #
115
+ # @param [String] key The key.
116
+ # @return [Hash] The document identified by the supplied key.
117
+ def lookup(key)
118
+ resp = create_request().get(build_index_lookup_url(key))
119
+ raise_on_http_error(resp)
120
+ doc = JSON.parse(resp.to_s)
121
+ doc.delete("@odata.context")
122
+ return doc
123
+ end
124
+
125
+ private
126
+ def build_index_definition_url
127
+ "https://#{self.service_name}.search.windows.net/indexes/#{self.index_name}?api-version=#{API_VERSION}"
128
+ end
129
+
130
+ def build_index_list_url
131
+ "https://#{self.service_name}.search.windows.net/indexes?api-version=#{API_VERSION}"
132
+ end
133
+
134
+ def build_indexing_url
135
+ "https://#{self.service_name}.search.windows.net/indexes/#{self.index_name}/docs/index?api-version=#{API_VERSION}"
136
+ end
137
+
138
+ def build_index_search_url(text, options)
139
+ query = {"search" => URI.encode_www_form_component(text)}.merge(options.to_hash)
140
+ "https://#{self.service_name}.search.windows.net/indexes/#{self.index_name}/docs?api-version=#{API_VERSION}&" << to_query_string(query)
141
+ end
142
+
143
+ def build_index_lookup_url(key)
144
+ "https://#{self.service_name}.search.windows.net/indexes/#{self.index_name}/docs('#{key}')?api-version=#{API_VERSION}"
145
+ end
146
+
147
+ def create_request
148
+ HTTP.headers({
149
+ "Content-Type" => "application/json",
150
+ "api-key" => self.api_key
151
+ })
152
+ end
153
+
154
+ def raise_on_http_error(response)
155
+ status_code = response.code
156
+ if status_code > 399
157
+ # 503 means the server is asking for backoff + retry
158
+ if status_code == 503
159
+ raise RetriableHttpError.new(status_code, response.to_s)
160
+ else
161
+ raise StandardError, "#{response.to_s}"
162
+ end
163
+ end
164
+ end
165
+
166
+ def validate_search_option(options)
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,41 @@
1
+ require 'nokogiri'
2
+
3
+ module AzureSearch
4
+
5
+ # Checks if the supplied value is a Boolean.
6
+ #
7
+ # @param [Object] value The value to check.
8
+ # @return [TrueClass,FalseClass] true or false.
9
+ def is_bool?(value); [true, false].include? value end
10
+
11
+ # Checks if the supplied text contains valid HTML.
12
+ #
13
+ # @param [String] text The text to check.
14
+ # @return [TrueClass,FalseClass] true or false.
15
+ def has_html?(text); Nokogiri::XML(text).errors.empty? end
16
+
17
+ # Converts a Hash into query parameters string.
18
+ #
19
+ # @param [Hash] params The hash to convert.
20
+ # @return [String] query string.
21
+ def to_query_string(params)
22
+ params.map{|k,v|
23
+ if v.nil?
24
+ k.to_s
25
+ elsif v.respond_to?(:to_ary)
26
+ v.to_ary.map{|w|
27
+ str = k.to_s.dup
28
+ unless w.nil?
29
+ str << '='
30
+ str << w.to_s
31
+ end
32
+ }.join('&')
33
+ else
34
+ str = k.to_s.dup
35
+ str << '='
36
+ str << v.to_s
37
+ end
38
+ }.join('&')
39
+ end
40
+
41
+ end
@@ -0,0 +1,3 @@
1
+ module AzureSearch
2
+ VERSION = "0.1.0".freeze
3
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: azure_search
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Faissal Elamraoui
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
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.15'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.15'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '12.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '12.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: '3.6'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.6'
83
+ description: This library allows you to interact with Microsoft Azure Search using
84
+ Ruby.
85
+ email:
86
+ - amr.faissal@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - lib/azure_search.rb
92
+ - lib/azure_search/errors.rb
93
+ - lib/azure_search/index_batch_operation.rb
94
+ - lib/azure_search/index_field.rb
95
+ - lib/azure_search/index_search_options.rb
96
+ - lib/azure_search/search_index_client.rb
97
+ - lib/azure_search/utils.rb
98
+ - lib/azure_search/version.rb
99
+ homepage: https://github.com/amrfaissal/azuresearch-ruby-client.git
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.6.11
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Microsoft Azure Search Client Library for Ruby.
123
+ test_files: []