elasticsearch_record 1.0.0

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +74 -0
  6. data/README.md +216 -0
  7. data/Rakefile +8 -0
  8. data/docs/CHANGELOG.md +44 -0
  9. data/docs/CODE_OF_CONDUCT.md +84 -0
  10. data/docs/LICENSE.txt +21 -0
  11. data/lib/active_record/connection_adapters/elasticsearch/column.rb +32 -0
  12. data/lib/active_record/connection_adapters/elasticsearch/database_statements.rb +149 -0
  13. data/lib/active_record/connection_adapters/elasticsearch/quoting.rb +38 -0
  14. data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +134 -0
  15. data/lib/active_record/connection_adapters/elasticsearch/type/format_string.rb +28 -0
  16. data/lib/active_record/connection_adapters/elasticsearch/type/multicast_value.rb +52 -0
  17. data/lib/active_record/connection_adapters/elasticsearch/type/object.rb +44 -0
  18. data/lib/active_record/connection_adapters/elasticsearch/type/range.rb +42 -0
  19. data/lib/active_record/connection_adapters/elasticsearch/type.rb +16 -0
  20. data/lib/active_record/connection_adapters/elasticsearch_adapter.rb +197 -0
  21. data/lib/arel/collectors/elasticsearch_query.rb +112 -0
  22. data/lib/arel/nodes/select_agg.rb +22 -0
  23. data/lib/arel/nodes/select_configure.rb +9 -0
  24. data/lib/arel/nodes/select_kind.rb +9 -0
  25. data/lib/arel/nodes/select_query.rb +20 -0
  26. data/lib/arel/visitors/elasticsearch.rb +589 -0
  27. data/lib/elasticsearch_record/base.rb +14 -0
  28. data/lib/elasticsearch_record/core.rb +59 -0
  29. data/lib/elasticsearch_record/extensions/relation.rb +15 -0
  30. data/lib/elasticsearch_record/gem_version.rb +17 -0
  31. data/lib/elasticsearch_record/instrumentation/controller_runtime.rb +39 -0
  32. data/lib/elasticsearch_record/instrumentation/log_subscriber.rb +70 -0
  33. data/lib/elasticsearch_record/instrumentation/railtie.rb +16 -0
  34. data/lib/elasticsearch_record/instrumentation.rb +17 -0
  35. data/lib/elasticsearch_record/model_schema.rb +43 -0
  36. data/lib/elasticsearch_record/patches/active_record/relation_merger_patch.rb +85 -0
  37. data/lib/elasticsearch_record/patches/arel/select_core_patch.rb +64 -0
  38. data/lib/elasticsearch_record/patches/arel/select_manager_patch.rb +91 -0
  39. data/lib/elasticsearch_record/patches/arel/select_statement_patch.rb +41 -0
  40. data/lib/elasticsearch_record/patches/arel/update_manager_patch.rb +46 -0
  41. data/lib/elasticsearch_record/patches/arel/update_statement_patch.rb +60 -0
  42. data/lib/elasticsearch_record/persistence.rb +80 -0
  43. data/lib/elasticsearch_record/query.rb +129 -0
  44. data/lib/elasticsearch_record/querying.rb +90 -0
  45. data/lib/elasticsearch_record/relation/calculation_methods.rb +155 -0
  46. data/lib/elasticsearch_record/relation/core_methods.rb +64 -0
  47. data/lib/elasticsearch_record/relation/query_clause.rb +43 -0
  48. data/lib/elasticsearch_record/relation/query_clause_tree.rb +94 -0
  49. data/lib/elasticsearch_record/relation/query_methods.rb +276 -0
  50. data/lib/elasticsearch_record/relation/result_methods.rb +222 -0
  51. data/lib/elasticsearch_record/relation/value_methods.rb +54 -0
  52. data/lib/elasticsearch_record/result.rb +236 -0
  53. data/lib/elasticsearch_record/statement_cache.rb +87 -0
  54. data/lib/elasticsearch_record/version.rb +10 -0
  55. data/lib/elasticsearch_record.rb +60 -0
  56. data/sig/elasticsearch_record.rbs +4 -0
  57. metadata +175 -0
@@ -0,0 +1,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticsearchRecord
4
+ class Result
5
+ include Enumerable
6
+
7
+ # creates an empty response
8
+ # @return [ElasticsearchRecord::Result (frozen)]
9
+ def self.empty
10
+ new(nil).freeze
11
+ end
12
+
13
+ attr_reader :response, :columns, :column_types
14
+
15
+ # initializes a new result object
16
+ # @param [Elasticsearch::API::Response, Object, nil] response
17
+ # @param [Array] columns
18
+ # @param [Hash] column_types
19
+ def initialize(response, columns = [], column_types = {})
20
+ # contains either the response or creates a empty hash (if nil)
21
+ @response = response.presence || {}
22
+
23
+ # used to build computed_results
24
+ @columns = columns
25
+
26
+ # used to cast values
27
+ @column_types = column_types
28
+ end
29
+
30
+ # returns the response duration time
31
+ # @return [Integer]
32
+ def took
33
+ response['took']
34
+ end
35
+
36
+ # returns the response total value.
37
+ # either chops the +total+ value directly from response, from hits or aggregations.
38
+ # @return [Integer]
39
+ def total
40
+ # chop total only
41
+ @total ||= _chop_total
42
+ end
43
+
44
+ # returns the response RAW hits hash.
45
+ # PLEASE NOTE: Does not return the nested hits (+response['hits']['hits']+) array!
46
+ # @return [ActiveSupport::HashWithIndifferentAccess, Hash]
47
+ def hits
48
+ response.key?('hits') ? response['hits'].with_indifferent_access : {}
49
+ end
50
+
51
+ # Returns the RAW values from the hits - aka. +rows+.
52
+ # PLEASE NOTE: The array will only contain the RAW data from each +_source+ (meta info like '_score' is not included)
53
+ # The +rows+ alias use used by the ActiveRecord ConnectionAdapters and must not be removed!
54
+ # @return [Array]
55
+ def results
56
+ return [] unless response['hits']
57
+
58
+ response['hits']['hits'].map { |result| result['_source'] }
59
+ end
60
+
61
+ alias_method :rows, :results
62
+
63
+ # returns the response RAW aggregations hash.
64
+ # @return [ActiveSupport::HashWithIndifferentAccess, Hash]
65
+ def aggregations
66
+ response.key?('aggregations') ? response['aggregations'].with_indifferent_access : {}
67
+ end
68
+
69
+ # Returns true if this result set includes the column named +name+.
70
+ # used by ActiveRecord
71
+ def includes_column?(name)
72
+ @columns&.include?(name)
73
+ end
74
+
75
+ # Returns the number of elements in the response array.
76
+ # Either uses the +hits+ length or the +responses+ length _(msearch)_.
77
+ # @return [Integer]
78
+ def length
79
+ if response.key?('hits')
80
+ response['hits']['hits'].length
81
+ elsif response.key?('responses')
82
+ # used by +msearch+
83
+ response['responses'].length
84
+ else
85
+ 0
86
+ end
87
+ end
88
+
89
+ # Calls the given block once for each element in row collection, passing
90
+ # row as parameter.
91
+ #
92
+ # Returns an +Enumerator+ if no block is given.
93
+ def each(&block)
94
+ if block_given?
95
+ computed_results.each(&block)
96
+ else
97
+ computed_results.to_enum { @computed_results.size }
98
+ end
99
+ end
100
+
101
+ # Returns true if there are no records, otherwise false.
102
+ def empty?
103
+ length == 0
104
+ end
105
+
106
+ # Returns an array of hashes representing each row record.
107
+ def to_ary
108
+ computed_results
109
+ end
110
+
111
+ alias :to_a :to_ary
112
+
113
+ def [](idx)
114
+ computed_results[idx]
115
+ end
116
+
117
+ # Returns the last record from the rows collection.
118
+ def last(n = nil)
119
+ n ? computed_results.last(n) : computed_results.last
120
+ end
121
+
122
+ # used by ActiveRecord
123
+ def result # :nodoc:
124
+ self
125
+ end
126
+
127
+ # used by ActiveRecord
128
+ def cancel # :nodoc:
129
+ self
130
+ end
131
+
132
+ # used by ActiveRecord
133
+ def cast_values(type_overrides = {})
134
+ # :nodoc:
135
+ if columns.one?
136
+ # Separated to avoid allocating an array per row
137
+ key = columns.first
138
+
139
+ type = if type_overrides.is_a?(Array)
140
+ type_overrides.first
141
+ else
142
+ column_type(columns.first, type_overrides)
143
+ end
144
+
145
+ computed_results.map do |result|
146
+ type.deserialize(result[key])
147
+ end
148
+ else
149
+ types = if type_overrides.is_a?(Array)
150
+ type_overrides
151
+ else
152
+ columns.map { |name| column_type(name, type_overrides) }
153
+ end
154
+
155
+ size = types.size
156
+
157
+ computed_results.map do |result|
158
+ Array.new(size) { |i|
159
+ key = columns[i]
160
+ types[i].deserialize(result[key])
161
+ }
162
+ end
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ # used by ActiveRecord
169
+ def column_type(name, type_overrides = {})
170
+ type_overrides.fetch(name, Type.default_value)
171
+ end
172
+
173
+ # chops total value from response
174
+ # @return [Integer]
175
+ def _chop_total
176
+ return self.response['total'] if self.response.key?('total')
177
+ return self.response['hits']['total']['value'] if self.response.key?('hits')
178
+ return self.response['aggregations'].count if self.response.key?('aggregations')
179
+
180
+ 0
181
+ end
182
+
183
+ # used for +msearch+ results
184
+ # @return [Array]
185
+ def _results_for_responses
186
+ response['responses'].map { |response| self.class.new(response, self.columns, self.column_types) }
187
+ end
188
+
189
+ # used for +search+ results
190
+ # @return [Array]
191
+ def _results_for_hits
192
+ # PLEASE NOTE: the 'hits' response has multiple nodes: BASE nodes & the +_source+ node.
193
+ # The real data is within the source node, but we also want the BASE nodes for possible score & type check
194
+ base_fields = ActiveRecord::ConnectionAdapters::ElasticsearchAdapter.base_structure_keys
195
+
196
+ # check for provided columns
197
+ if @columns.present?
198
+ # We freeze the strings to prevent them getting duped when
199
+ # used as keys in ActiveRecord::Base's @attributes hash.
200
+ # ALSO IMPORTANT: remove base_fields from possible provided columns
201
+ columns = @columns ? (@columns - base_fields).map(&:-@) : []
202
+
203
+ # this is the hashed result array
204
+ response['hits']['hits'].map { |doc|
205
+ result = doc.slice(*base_fields)
206
+ columns.each do |column|
207
+ result[column] = doc['_source'][column]
208
+ end
209
+
210
+ result
211
+ }
212
+ else
213
+ # if we don't have any columns we just resolve the _source data as it is
214
+ # this might end up in unknown (but mapped) attributes (if they are stored as nil in ES)
215
+
216
+ # this is the hashed result array
217
+ response['hits']['hits'].map { |doc|
218
+ doc.slice(*base_fields).merge(doc['_source'])
219
+ }
220
+ end
221
+ end
222
+
223
+ # builds computed results (used to build ActiveRecord models)
224
+ # @return [Array]
225
+ def computed_results
226
+ @computed_results ||= if response.key?('hits')
227
+ _results_for_hits
228
+ elsif response.key?('responses')
229
+ # used by +msearch+
230
+ _results_for_responses
231
+ else
232
+ []
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticsearchRecord
4
+ class StatementCache < ActiveRecord::StatementCache
5
+
6
+ class PartialQuery < ActiveRecord::StatementCache::PartialQuery # :nodoc:
7
+ def initialize(values)
8
+ @values = values
9
+ # no need to create indexes
10
+ end
11
+
12
+ def sql_for(binds, connection)
13
+ # dup original array
14
+ claims = @values.dup
15
+
16
+ # substitute binds
17
+ claims.each do |claim|
18
+ # action, args = claim
19
+ claim[1] = deep_substitute_binds(claim[1], binds, connection)
20
+ end
21
+
22
+ # build a new query collector
23
+ collector = ::Arel::Collectors::ElasticsearchQuery.new
24
+
25
+ claims.each do |claim|
26
+ collector << claim
27
+ end
28
+
29
+ collector
30
+ end
31
+
32
+ private
33
+
34
+ def deep_substitute_binds(thing, binds, connection)
35
+ if thing.is_a?(ActiveRecord::StatementCache::Substitute)
36
+ value = binds.shift
37
+ if ActiveModel::Attribute === value
38
+ value = value.value_for_database
39
+ end
40
+ connection.quote(value)
41
+ elsif thing.is_a?(Hash)
42
+ thing.transform_values { |val|
43
+ deep_substitute_binds(val, binds, connection)
44
+ }
45
+ elsif thing.is_a?(Array)
46
+ thing.map { |val|
47
+ deep_substitute_binds(val, binds, connection)
48
+ }
49
+ else
50
+ thing
51
+ end
52
+ end
53
+ end
54
+
55
+ class PartialQueryCollector < ActiveRecord::StatementCache::PartialQueryCollector # :nodoc:
56
+ def add_bind(obj)
57
+ super
58
+
59
+ # only add binds, no parts - so we need to remove the previously set part
60
+ @parts.pop
61
+
62
+ self
63
+ end
64
+
65
+ def add_binds(binds, proc_for_binds = nil)
66
+ super
67
+
68
+ # only add binds, no parts - so we need to remove the previously set part
69
+ if binds.size == 1
70
+ @parts.pop
71
+ else
72
+ @parts.pop((binds.size * 2) - 1)
73
+ end
74
+
75
+ self
76
+ end
77
+ end
78
+
79
+ def self.partial_query(values)
80
+ PartialQuery.new(values)
81
+ end
82
+
83
+ def self.partial_query_collector
84
+ PartialQueryCollector.new
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'gem_version'
4
+
5
+ module ElasticsearchRecord
6
+ # Returns the version of the currently loaded Gem as a <tt>Gem::Version</tt>
7
+ def self.version
8
+ gem_version
9
+ end
10
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # VERSION
4
+ require_relative 'elasticsearch_record/version'
5
+
6
+ require 'active_record'
7
+
8
+ # new arel
9
+ require 'arel/collectors/elasticsearch_query'
10
+ require 'arel/nodes/select_agg'
11
+ require 'arel/nodes/select_configure'
12
+ require 'arel/nodes/select_kind'
13
+ require 'arel/nodes/select_query'
14
+ require 'arel/visitors/elasticsearch'
15
+
16
+ # new adapter
17
+ require 'active_record/connection_adapters/elasticsearch_adapter'
18
+
19
+ module ElasticsearchRecord
20
+ extend ActiveSupport::Autoload
21
+
22
+ eager_autoload do
23
+ autoload :Base
24
+ autoload :Core
25
+ autoload :ModelSchema
26
+ autoload :Persistence
27
+ autoload :Querying
28
+ autoload :Query
29
+ autoload :Result
30
+ autoload :StatementCache
31
+ end
32
+
33
+ module Extensions
34
+ extend ActiveSupport::Autoload
35
+
36
+ autoload :Relation
37
+ end
38
+
39
+ module Relation
40
+ extend ActiveSupport::Autoload
41
+
42
+ autoload :CalculationMethods
43
+ autoload :CoreMethods
44
+ autoload :QueryClause
45
+ autoload :QueryClauseTree
46
+ autoload :QueryMethods
47
+ autoload :ResultMethods
48
+ autoload :ValueMethods
49
+ end
50
+ end
51
+
52
+ ActiveSupport.on_load(:active_record) do
53
+ # load patches
54
+ require 'elasticsearch_record/patches/active_record/relation_merger_patch'
55
+ require 'elasticsearch_record/patches/arel/select_core_patch'
56
+ require 'elasticsearch_record/patches/arel/select_manager_patch'
57
+ require 'elasticsearch_record/patches/arel/select_statement_patch'
58
+ require 'elasticsearch_record/patches/arel/update_manager_patch'
59
+ require 'elasticsearch_record/patches/arel/update_statement_patch'
60
+ end
@@ -0,0 +1,4 @@
1
+ module ElasticsearchRecord
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elasticsearch_record
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tobias Gonsior
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-10-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.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: '8.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '8.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.21'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.21'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13.0'
83
+ description: 'ElasticsearchRecord is a ActiveRecord-fork and tries to provide the
84
+ same functionality for Elasticsearch.
85
+
86
+ '
87
+ email:
88
+ - info@ruby-smart.org
89
+ executables: []
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - ".rspec"
94
+ - ".ruby-version"
95
+ - Gemfile
96
+ - Gemfile.lock
97
+ - README.md
98
+ - Rakefile
99
+ - docs/CHANGELOG.md
100
+ - docs/CODE_OF_CONDUCT.md
101
+ - docs/LICENSE.txt
102
+ - lib/active_record/connection_adapters/elasticsearch/column.rb
103
+ - lib/active_record/connection_adapters/elasticsearch/database_statements.rb
104
+ - lib/active_record/connection_adapters/elasticsearch/quoting.rb
105
+ - lib/active_record/connection_adapters/elasticsearch/schema_statements.rb
106
+ - lib/active_record/connection_adapters/elasticsearch/type.rb
107
+ - lib/active_record/connection_adapters/elasticsearch/type/format_string.rb
108
+ - lib/active_record/connection_adapters/elasticsearch/type/multicast_value.rb
109
+ - lib/active_record/connection_adapters/elasticsearch/type/object.rb
110
+ - lib/active_record/connection_adapters/elasticsearch/type/range.rb
111
+ - lib/active_record/connection_adapters/elasticsearch_adapter.rb
112
+ - lib/arel/collectors/elasticsearch_query.rb
113
+ - lib/arel/nodes/select_agg.rb
114
+ - lib/arel/nodes/select_configure.rb
115
+ - lib/arel/nodes/select_kind.rb
116
+ - lib/arel/nodes/select_query.rb
117
+ - lib/arel/visitors/elasticsearch.rb
118
+ - lib/elasticsearch_record.rb
119
+ - lib/elasticsearch_record/base.rb
120
+ - lib/elasticsearch_record/core.rb
121
+ - lib/elasticsearch_record/extensions/relation.rb
122
+ - lib/elasticsearch_record/gem_version.rb
123
+ - lib/elasticsearch_record/instrumentation.rb
124
+ - lib/elasticsearch_record/instrumentation/controller_runtime.rb
125
+ - lib/elasticsearch_record/instrumentation/log_subscriber.rb
126
+ - lib/elasticsearch_record/instrumentation/railtie.rb
127
+ - lib/elasticsearch_record/model_schema.rb
128
+ - lib/elasticsearch_record/patches/active_record/relation_merger_patch.rb
129
+ - lib/elasticsearch_record/patches/arel/select_core_patch.rb
130
+ - lib/elasticsearch_record/patches/arel/select_manager_patch.rb
131
+ - lib/elasticsearch_record/patches/arel/select_statement_patch.rb
132
+ - lib/elasticsearch_record/patches/arel/update_manager_patch.rb
133
+ - lib/elasticsearch_record/patches/arel/update_statement_patch.rb
134
+ - lib/elasticsearch_record/persistence.rb
135
+ - lib/elasticsearch_record/query.rb
136
+ - lib/elasticsearch_record/querying.rb
137
+ - lib/elasticsearch_record/relation/calculation_methods.rb
138
+ - lib/elasticsearch_record/relation/core_methods.rb
139
+ - lib/elasticsearch_record/relation/query_clause.rb
140
+ - lib/elasticsearch_record/relation/query_clause_tree.rb
141
+ - lib/elasticsearch_record/relation/query_methods.rb
142
+ - lib/elasticsearch_record/relation/result_methods.rb
143
+ - lib/elasticsearch_record/relation/value_methods.rb
144
+ - lib/elasticsearch_record/result.rb
145
+ - lib/elasticsearch_record/statement_cache.rb
146
+ - lib/elasticsearch_record/version.rb
147
+ - sig/elasticsearch_record.rbs
148
+ homepage: https://github.com/ruby-smart/elasticsearch_record
149
+ licenses:
150
+ - MIT
151
+ metadata:
152
+ allowed_push_host: https://rubygems.org
153
+ homepage_uri: https://github.com/ruby-smart/elasticsearch_record
154
+ source_code_uri: https://github.com/ruby-smart/elasticsearch_record
155
+ changelog_uri: https://github.com/ruby-smart/elasticsearch_record/blob/main/docs/CHANGELOG.md
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: 2.7.0
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubygems_version: 3.3.7
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: ActiveRecord functionality for Elasticsearch indexes & documents.
175
+ test_files: []