stretchy-model 0.1.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 (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: []