stretchy-model 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/.yardopts +1 -0
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +146 -0
  9. data/Rakefile +4 -0
  10. data/containers/Dockerfile.elasticsearch +7 -0
  11. data/containers/Dockerfile.opensearch +19 -0
  12. data/docker-compose.yml +52 -0
  13. data/lib/active_model/type/array.rb +13 -0
  14. data/lib/active_model/type/hash.rb +15 -0
  15. data/lib/rails/instrumentation/publishers.rb +29 -0
  16. data/lib/rails/instrumentation/railtie.rb +29 -0
  17. data/lib/stretchy/associations/associated_validator.rb +17 -0
  18. data/lib/stretchy/associations/elastic_relation.rb +38 -0
  19. data/lib/stretchy/associations.rb +161 -0
  20. data/lib/stretchy/common.rb +33 -0
  21. data/lib/stretchy/delegation/delegate_cache.rb +131 -0
  22. data/lib/stretchy/delegation/gateway_delegation.rb +43 -0
  23. data/lib/stretchy/indexing/bulk.rb +48 -0
  24. data/lib/stretchy/model/callbacks.rb +31 -0
  25. data/lib/stretchy/model/serialization.rb +20 -0
  26. data/lib/stretchy/null_relation.rb +53 -0
  27. data/lib/stretchy/persistence.rb +43 -0
  28. data/lib/stretchy/querying.rb +20 -0
  29. data/lib/stretchy/record.rb +57 -0
  30. data/lib/stretchy/refreshable.rb +15 -0
  31. data/lib/stretchy/relation.rb +169 -0
  32. data/lib/stretchy/relations/finder_methods.rb +39 -0
  33. data/lib/stretchy/relations/merger.rb +179 -0
  34. data/lib/stretchy/relations/query_builder.rb +265 -0
  35. data/lib/stretchy/relations/query_methods.rb +578 -0
  36. data/lib/stretchy/relations/search_option_methods.rb +34 -0
  37. data/lib/stretchy/relations/spawn_methods.rb +60 -0
  38. data/lib/stretchy/repository.rb +10 -0
  39. data/lib/stretchy/scoping/default.rb +134 -0
  40. data/lib/stretchy/scoping/named.rb +68 -0
  41. data/lib/stretchy/scoping/scope_registry.rb +34 -0
  42. data/lib/stretchy/scoping.rb +28 -0
  43. data/lib/stretchy/shared_scopes.rb +34 -0
  44. data/lib/stretchy/utils.rb +69 -0
  45. data/lib/stretchy/version.rb +5 -0
  46. data/lib/stretchy.rb +38 -0
  47. data/sig/stretchy.rbs +4 -0
  48. data/stretchy.logo.png +0 -0
  49. metadata +247 -0
@@ -0,0 +1,134 @@
1
+ module Stretchy
2
+ module Scoping
3
+ module Default
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # Stores the default scope for the class.
8
+ class_attribute :default_scopes, instance_writer: false, instance_predicate: false
9
+
10
+ self.default_scopes = []
11
+ end
12
+
13
+ module ClassMethods
14
+ # Returns a scope for the model without the previously set scopes.
15
+ #
16
+ # class Post < ActiveRecord::Base
17
+ # def self.default_scope
18
+ # where published: true
19
+ # end
20
+ # end
21
+ #
22
+ # Post.all # Fires "SELECT * FROM posts WHERE published = true"
23
+ # Post.unscoped.all # Fires "SELECT * FROM posts"
24
+ # Post.where(published: false).unscoped.all # Fires "SELECT * FROM posts"
25
+ #
26
+ # This method also accepts a block. All queries inside the block will
27
+ # not use the previously set scopes.
28
+ #
29
+ # Post.unscoped {
30
+ # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
31
+ # }
32
+ def unscoped
33
+ block_given? ? relation.scoping { yield } : relation
34
+ end
35
+
36
+ def before_remove_const #:nodoc:
37
+ self.current_scope = nil
38
+ end
39
+
40
+ protected
41
+
42
+ # Use this macro in your model to set a default scope for all operations on
43
+ # the model.
44
+ #
45
+ # class Article < ActiveRecord::Base
46
+ # default_scope { where(published: true) }
47
+ # end
48
+ #
49
+ # Article.all # => SELECT * FROM articles WHERE published = true
50
+ #
51
+ # The +default_scope+ is also applied while creating/building a record.
52
+ # It is not applied while updating a record.
53
+ #
54
+ # Article.new.published # => true
55
+ # Article.create.published # => true
56
+ #
57
+ # (You can also pass any object which responds to +call+ to the
58
+ # +default_scope+ macro, and it will be called when building the
59
+ # default scope.)
60
+ #
61
+ # If you use multiple +default_scope+ declarations in your model then
62
+ # they will be merged together:
63
+ #
64
+ # class Article < ActiveRecord::Base
65
+ # default_scope { where(published: true) }
66
+ # default_scope { where(rating: 'G') }
67
+ # end
68
+ #
69
+ # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
70
+ #
71
+ # This is also the case with inheritance and module includes where the
72
+ # parent or module defines a +default_scope+ and the child or including
73
+ # class defines a second one.
74
+ #
75
+ # If you need to do more complex things with a default scope, you can
76
+ # alternatively define it as a class method:
77
+ #
78
+ # class Article < ActiveRecord::Base
79
+ # def self.default_scope
80
+ # # Should return a scope, you can call 'super' here etc.
81
+ # end
82
+ # end
83
+ def default_scope(scope = nil)
84
+ scope = Proc.new if block_given?
85
+
86
+ if scope.is_a?(Relation) || !scope.respond_to?(:call)
87
+ raise ArgumentError,
88
+ "Support for calling #default_scope without a block is removed. For example instead " \
89
+ "of `default_scope where(color: 'red')`, please use " \
90
+ "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
91
+ "self.default_scope.)"
92
+ end
93
+
94
+ self.default_scopes += [scope]
95
+ end
96
+
97
+ def build_default_scope(base_rel = relation) # :nodoc:
98
+ if !self.is_a?(method(:default_scope).owner)
99
+ # The user has defined their own default scope method, so call that
100
+ evaluate_default_scope { default_scope }
101
+ elsif default_scopes.any?
102
+ evaluate_default_scope do
103
+ default_scopes.inject(base_rel) do |default_scope, scope|
104
+ default_scope.merge(base_rel.scoping { scope.call })
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def ignore_default_scope? # :nodoc:
111
+ ScopeRegistry.value_for(:ignore_default_scope, self)
112
+ end
113
+
114
+ def ignore_default_scope=(ignore) # :nodoc:
115
+ ScopeRegistry.set_value_for(:ignore_default_scope, self, ignore)
116
+ end
117
+
118
+ # The ignore_default_scope flag is used to prevent an infinite recursion
119
+ # situation where a default scope references a scope which has a default
120
+ # scope which references a scope...
121
+ def evaluate_default_scope # :nodoc:
122
+ return if ignore_default_scope?
123
+
124
+ begin
125
+ self.ignore_default_scope = true
126
+ yield
127
+ ensure
128
+ self.ignore_default_scope = false
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,68 @@
1
+ module Stretchy
2
+ module Scoping
3
+ module Named
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def all(options={})
8
+ if current_scope
9
+ current_scope.clone
10
+ else
11
+ default_scoped.size(default_size)
12
+ end
13
+ end
14
+
15
+ def default_scoped # :nodoc:
16
+ relation.merge(build_default_scope)
17
+ end
18
+
19
+ # Collects attributes from scopes that should be applied when creating
20
+ # an AR instance for the particular class this is called on.
21
+ def scope_attributes # :nodoc:
22
+ all.scope_for_create
23
+ end
24
+
25
+ # Are there default attributes associated with this scope?
26
+ def scope_attributes? # :nodoc:
27
+ current_scope || default_scopes.any?
28
+ end
29
+
30
+ def scope(name, body, &block)
31
+ if dangerous_class_method?(name)
32
+ raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
33
+ "on the model \"#{self.name}\", but there's already defined " \
34
+ "a class method with the same name."
35
+ end
36
+
37
+ extension = Module.new(&block) if block
38
+
39
+ singleton_class.send(:define_method, name) do |*args|
40
+ scope = all.scoping { body.call(*args) }
41
+ scope = scope.extending(extension) if extension
42
+
43
+ scope || all
44
+ end
45
+ end
46
+
47
+ BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
48
+
49
+ private
50
+ def dangerous_class_method?(method_name)
51
+ BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Model, self.superclass)
52
+ end
53
+
54
+ def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
55
+ if klass.respond_to?(name, true)
56
+ if superklass.respond_to?(name, true)
57
+ klass.method(name).owner != superklass.method(name).owner
58
+ else
59
+ true
60
+ end
61
+ else
62
+ false
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,34 @@
1
+ module Stretchy
2
+ module Scoping
3
+ class ScopeRegistry # :nodoc:
4
+ # extend ActiveSupport::PerThreadRegistry
5
+ thread_mattr_accessor :registry
6
+
7
+ VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
8
+
9
+ def initialize
10
+ @registry = Hash.new { |hash, key| hash[key] = {} }
11
+ end
12
+
13
+ # Obtains the value for a given +scope_name+ and +variable_name+.
14
+ def value_for(scope_type, variable_name)
15
+ raise_invalid_scope_type!(scope_type)
16
+ @registry[scope_type][variable_name]
17
+ end
18
+
19
+ # Sets the +value+ for a given +scope_type+ and +variable_name+.
20
+ def set_value_for(scope_type, variable_name, value)
21
+ raise_invalid_scope_type!(scope_type)
22
+ @registry[scope_type][variable_name] = value
23
+ end
24
+
25
+ private
26
+
27
+ def raise_invalid_scope_type!(scope_type)
28
+ if !VALID_SCOPE_TYPES.include?(scope_type)
29
+ raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ module Stretchy
2
+ module Scoping
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Default
7
+ include Named
8
+ end
9
+
10
+ module ClassMethods
11
+ def base_class
12
+ self
13
+ end
14
+
15
+ def current_scope
16
+ # ScopeRegistry.new.registry[:current_scope][base_class.to_s]
17
+ ScopeRegistry.new.value_for(:current_scope, base_class.to_s)
18
+ end
19
+
20
+ def current_scope=(scope) #:nodoc:
21
+ # ScopeRegistry.new.registry[:current_scope][base_class.to_s] = scope
22
+ ScopeRegistry.new.set_value_for(:current_scope, base_class.to_s, scope)
23
+ end
24
+ end
25
+
26
+
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ module Stretchy
2
+ module SharedScopes
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+
7
+ scope :between, lambda { |range| filter(:range, "date" => {gte: range.begin, lte: range.end}) }
8
+ scope :using_time_based_indices, lambda { |range| search_options(index: time_based_indices(range)) }
9
+
10
+ end
11
+
12
+ class_methods do
13
+
14
+ # Helpful when you want to search across multiple indices when there are
15
+ # many indices for a given model. It significantly reduces the load on the cluster.
16
+ #
17
+ # @example Narrow search across indices for the last 2 months
18
+ #
19
+ # class Twitter < Stretchy::Model
20
+ # scope :monthly_indices, lambda { |range| search_options(index: time_based_indices(range)) }
21
+ # end
22
+ #
23
+ # Twitter.monthly_indices(2.months.ago..1.day.ago)
24
+ # => search_url: "/twitter_2024_01*,twitter_2024_02*,twitter_2024_03*/_search"
25
+ #
26
+ def time_based_indices(range, format = "%Y_%m")
27
+ (range.begin.to_datetime...range.end.to_datetime).map do |d|
28
+ "#{self.index_name.gsub(/\*/, "")}#{d.utc.strftime("#{format}*")}"
29
+ end.uniq.join(",")
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,69 @@
1
+ module Stretchy
2
+ module Utils
3
+ extend ActiveSupport::Concern
4
+
5
+
6
+ def self.to_curl(klass, arguments = {}, end_point = "_search")
7
+ host = klass.gateway.client.transport.transport.hosts&.first || klass.gateway.client.transport.transport.options[:url]
8
+ arguments[:index] = "_all" if !arguments[:index] && arguments[:type]
9
+
10
+ valid_params = [
11
+ :analyzer,
12
+ :analyze_wildcard,
13
+ :default_operator,
14
+ :df,
15
+ :explain,
16
+ :fields,
17
+ :from,
18
+ :ignore_indices,
19
+ :ignore_unavailable,
20
+ :allow_no_indices,
21
+ :expand_wildcards,
22
+ :lenient,
23
+ :lowercase_expanded_terms,
24
+ :preference,
25
+ :q,
26
+ :routing,
27
+ :scroll,
28
+ :search_type,
29
+ :size,
30
+ :sort,
31
+ :source,
32
+ :_source,
33
+ :_source_include,
34
+ :_source_exclude,
35
+ :stats,
36
+ :suggest_field,
37
+ :suggest_mode,
38
+ :suggest_size,
39
+ :suggest_text,
40
+ :timeout,
41
+ :version,
42
+ ]
43
+
44
+ method = "GET"
45
+ path = Elasticsearch::API::Utils.__pathify(Elasticsearch::API::Utils.__listify(arguments[:index]), end_point)
46
+
47
+ params = Elasticsearch::API::Utils.__validate_and_extract_params arguments, valid_params
48
+ body = arguments[:body]
49
+
50
+ params[:fields] = Elasticsearch::API::Utils.__listify(params[:fields]) if params[:fields]
51
+
52
+ url = path
53
+
54
+ unless host.is_a? String
55
+ host_parts = "#{host[:protocol].to_s}://#{host[:host]}"
56
+ host_parts = "#{host_parts}:#{host[:port]}" if host[:port]
57
+ else
58
+ host_parts = host
59
+ end
60
+
61
+ trace_url = "#{host_parts}/#{url}"
62
+ trace_url += "?#{::Faraday::Utils::ParamsHash[params].to_query}" unless params.blank?
63
+ trace_body = body ? " -d '#{body.to_json}'" : ""
64
+
65
+ "curl -X#{method.to_s.upcase} '#{CGI.unescape(trace_url)}'#{trace_body}\n"
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stretchy
4
+ VERSION = "0.1.0"
5
+ end
data/lib/stretchy.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ require 'zeitwerk'
3
+ require 'amazing_print'
4
+ require 'rainbow'
5
+ require 'jbuilder'
6
+ # require 'elasticsearch/rails'
7
+ require 'elasticsearch/model'
8
+ require 'elasticsearch/persistence'
9
+ # require 'active_support/concern'
10
+ require 'active_model'
11
+ require 'active_support/all'
12
+ require 'active_model/type/array'
13
+ require 'active_model/type/hash'
14
+
15
+ ActiveModel::Type.register(:array, ActiveModel::Type::Array)
16
+ ActiveModel::Type.register(:hash, ActiveModel::Type::Hash)
17
+
18
+ require_relative "stretchy/version"
19
+ require_relative "stretchy/instrumentation/railtie" if defined?(Rails)
20
+
21
+ module Stretchy
22
+ module Errors
23
+ class QueryOptionMissing < StandardError; end
24
+ end
25
+
26
+ class << self
27
+ def logger
28
+ @logger ||= Logger.new(STDOUT)
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ loader = Zeitwerk::Loader.new
35
+ loader.tag = File.basename(__FILE__, ".rb")
36
+ loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
37
+ loader.push_dir(__dir__)
38
+ loader.setup
data/sig/stretchy.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Stretchy
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
data/stretchy.logo.png ADDED
Binary file
metadata ADDED
@@ -0,0 +1,247 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stretchy-model
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Spencer Markowski
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-03-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: zeitwerk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: elasticsearch-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '7.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '7.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: elasticsearch-persistence
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '7.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '7.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: elasticsearch-model
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '7.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '7.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rainbow
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: amazing_print
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: jbuilder
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.11'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.11'
111
+ - !ruby/object:Gem::Dependency
112
+ name: virtus
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.9'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.9'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.21.2
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 0.21.2
153
+ - !ruby/object:Gem::Dependency
154
+ name: yard
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 0.9.36
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 0.9.36
167
+ description: Provides a familiar ActiveRecord-like interface for working with Elasticsearch
168
+ email:
169
+ - spencer.markowski@theablefew.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".rspec"
175
+ - ".yardopts"
176
+ - CHANGELOG.md
177
+ - CODE_OF_CONDUCT.md
178
+ - Gemfile
179
+ - LICENSE.txt
180
+ - README.md
181
+ - Rakefile
182
+ - containers/Dockerfile.elasticsearch
183
+ - containers/Dockerfile.opensearch
184
+ - docker-compose.yml
185
+ - lib/active_model/type/array.rb
186
+ - lib/active_model/type/hash.rb
187
+ - lib/rails/instrumentation/publishers.rb
188
+ - lib/rails/instrumentation/railtie.rb
189
+ - lib/stretchy.rb
190
+ - lib/stretchy/associations.rb
191
+ - lib/stretchy/associations/associated_validator.rb
192
+ - lib/stretchy/associations/elastic_relation.rb
193
+ - lib/stretchy/common.rb
194
+ - lib/stretchy/delegation/delegate_cache.rb
195
+ - lib/stretchy/delegation/gateway_delegation.rb
196
+ - lib/stretchy/indexing/bulk.rb
197
+ - lib/stretchy/model/callbacks.rb
198
+ - lib/stretchy/model/serialization.rb
199
+ - lib/stretchy/null_relation.rb
200
+ - lib/stretchy/persistence.rb
201
+ - lib/stretchy/querying.rb
202
+ - lib/stretchy/record.rb
203
+ - lib/stretchy/refreshable.rb
204
+ - lib/stretchy/relation.rb
205
+ - lib/stretchy/relations/finder_methods.rb
206
+ - lib/stretchy/relations/merger.rb
207
+ - lib/stretchy/relations/query_builder.rb
208
+ - lib/stretchy/relations/query_methods.rb
209
+ - lib/stretchy/relations/search_option_methods.rb
210
+ - lib/stretchy/relations/spawn_methods.rb
211
+ - lib/stretchy/repository.rb
212
+ - lib/stretchy/scoping.rb
213
+ - lib/stretchy/scoping/default.rb
214
+ - lib/stretchy/scoping/named.rb
215
+ - lib/stretchy/scoping/scope_registry.rb
216
+ - lib/stretchy/shared_scopes.rb
217
+ - lib/stretchy/utils.rb
218
+ - lib/stretchy/version.rb
219
+ - sig/stretchy.rbs
220
+ - stretchy.logo.png
221
+ homepage: https://github.com/theablefew/stretchy
222
+ licenses:
223
+ - MIT
224
+ metadata:
225
+ homepage_uri: https://github.com/theablefew/stretchy
226
+ source_code_uri: https://github.com/theablefew/stretchy
227
+ changelog_uri: https://github.com/theablefew/stretchy/blob/main/CHANGELOG.md
228
+ post_install_message:
229
+ rdoc_options: []
230
+ require_paths:
231
+ - lib
232
+ required_ruby_version: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: 2.6.0
237
+ required_rubygems_version: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - ">="
240
+ - !ruby/object:Gem::Version
241
+ version: '0'
242
+ requirements: []
243
+ rubygems_version: 3.3.7
244
+ signing_key:
245
+ specification_version: 4
246
+ summary: Rails ORM for Elasticsearch
247
+ test_files: []