stretchy-model 0.1.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a955580b683d3dc991c7a1f92a5a9508fd10a4f9e8a116c2c92150ae4fdd43a3
4
- data.tar.gz: 30262c4a0d4e8d5e43a74ab707b3e501d8e5e627be16600687526885b4520cf3
3
+ metadata.gz: a85f7bffcc11c54366d4cae7fec47b91341425e06b7f1c8a6269e0b23d4e649b
4
+ data.tar.gz: cd0b9314fa734aa7af7179f12ed00626a17bc869186f82088158bfca87c8d915
5
5
  SHA512:
6
- metadata.gz: ce944998d1e6ae225f85fa7ecbccea8f940e3fcee99c8450b79f89b3138bedcddec7b69d62de8285983c21dbc4dc068c4fa42157d94484065a8948c118c9bd95
7
- data.tar.gz: 465d02c93b645b480bc4e627d4323d557ab6574dbefc2efc83de453aa246d38785ccb09cebf8d651e3f345d68e851e1197f32314bec84d13a379320dc468c3eb
6
+ metadata.gz: e193c837695100177eac9888841b14243596dfc9694515696ab1a9934174287c821cf52b2ca165e3f3eda04de9ba16c7c7330cb187641d71229d322c06674977
7
+ data.tar.gz: 21ace7b64f85d505a05b61b89b5b85d9d2af34941b57672da45f70e6c8736e8b9451c59891fef7d0795f8b54076cffd88bfc141fcd4da1c1f8ffaddcf8276697
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- stretchy
1
+ stretchy-model
2
2
  ===
3
3
 
4
4
  <p>
@@ -112,11 +112,37 @@ Blanket (6.322ms) curl -X GET 'http://localhost:9200/blankets/_search?size=1' -d
112
112
 
113
113
  Install the gem and add to the application's Gemfile by executing:
114
114
 
115
- $ bundle add stretchy
115
+ $ bundle add stretchy-model
116
116
 
117
117
  If bundler is not being used to manage dependencies, install the gem by executing:
118
118
 
119
- $ gem install stretchy
119
+ $ gem install stretchy-model
120
+
121
+ <details>
122
+ <summary>Rails Configuration</summary>
123
+
124
+
125
+
126
+ ```sh
127
+ rails credentials:edit
128
+ ```
129
+
130
+ #### Add elasticsearch credentials
131
+ ```yaml
132
+ elasticsearch:
133
+ url: localhost:9200
134
+ ```
135
+
136
+ #### Create an initializer
137
+ <p><sub><em>config/initializers/stretchy.rb</em></sub></p>
138
+
139
+ ```ruby {file=config/initializers/stretchy.rb}
140
+ Stretchy.configure do |config|
141
+ config.client = Elasticsearch::Client.new url: Rails.application.credentials.elasticsearch.url, log: true
142
+ end
143
+ ```
144
+ </details>
145
+
120
146
 
121
147
  ## Development
122
148
 
@@ -128,6 +154,10 @@ After checking out the repo, run `bin/setup` to install dependencies. You can al
128
154
  > Full documentation on [Elasticsearch Query DSL and Aggregation options](https://github.com/elastic/elasticsearch-rails/tree/main/elasticsearch-persistence)
129
155
 
130
156
  ## Testing
157
+ <details>
158
+ <summary>Elasticsearch</summary>
159
+
160
+
131
161
  ```
132
162
  docker-compose up elasticsearch
133
163
  ```
@@ -136,6 +166,21 @@ docker-compose up elasticsearch
136
166
  bundle exec rspec
137
167
  ```
138
168
 
169
+ </details>
170
+
171
+ <details>
172
+ <summary>Opensearch</summary>
173
+
174
+
175
+ ```
176
+ docker-compose up opensearch
177
+ ```
178
+
179
+ ```
180
+ ENV['BACKEND']=opensearch bundle rspec
181
+ ```
182
+ </details>
183
+
139
184
  ## Contributing
140
185
 
141
186
  Bug reports and pull requests are welcome on GitHub at https://github.com/theablefew/stretchy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/theablefew/stretchy/blob/master/CODE_OF_CONDUCT.md).
@@ -14,7 +14,7 @@ module Rails
14
14
  end
15
15
 
16
16
  def search_with_instrumentation!(query_or_definition, options={})
17
- ActiveSupport::Notifications.instrument "search.elasticsearch",
17
+ ActiveSupport::Notifications.instrument "search.stretchy",
18
18
  name: "Search",
19
19
  klass: self.base_class.to_s,
20
20
  search: {index: self.index_name, body: query_or_definition }.merge(options) do
@@ -2,27 +2,21 @@ module Stretchy
2
2
  module Instrumentation
3
3
 
4
4
  class Railtie < ::Rails::Railtie
5
- include ActionView::Helpers::NumberHelper
6
- def time_diff(start, finish)
7
- begin
8
- ((finish.to_time - start.to_time) * 1000).to_s(:rounded, precision: 5, strip_insignificant_zeros: true)
9
- rescue
10
- number_with_precision((finish.to_time - start.to_time) * 1000, precision: 5, strip_insignificant_zeros: true)
11
- end
12
- end
13
5
 
14
- ActiveSupport::Notifications.subscribe 'query.elasticsearch' do |name, start, finish, id, payload|
15
- ::Rails.logger.debug([" #{payload[:name]}".bright, "(#{time_diff(start,finish)}ms)",payload[:query]].join(" ").color(:yellow))
16
- end
6
+ require 'rails/instrumentation/publishers'
7
+ ActiveSupport::Notifications.subscribe 'search.stretchy' do |name, start, finish, id, payload|
8
+ message = [
9
+ Rainbow(" #{payload[:klass]}").bright,
10
+ "(#{(finish.to_time - start.to_time).round(2)}ms)",
11
+ Stretchy::Utils.to_curl(payload[:klass].constantize, payload[:search])
12
+ ].join(" ")
13
+ ::Rails.logger.debug(Rainbow(message).color(:yellow))
14
+ end
17
15
 
18
- ActiveSupport::Notifications.subscribe 'cache.query.elasticsearch' do |name, start, finish, id, payload|
19
- ::Rails.logger.debug([" #{payload[:name]}".bright, "CACHE".color(:mediumpurple).bright ,"(#{time_diff(start,finish)}ms)", payload[:query]].join(" ").color(:yellow))
20
- end
21
-
22
- initializer 'stretchy.set_defaults' do
23
- config.elasticsearch_cache_store = :redis_store
24
- config.elasticsearch_expire_cache_in = 15.minutes
25
- end
16
+ # initializer 'stretchy.set_defaults' do
17
+ # config.elasticsearch_cache_store = :redis_store
18
+ # config.elasticsearch_expire_cache_in = 15.minutes
19
+ # end
26
20
 
27
21
  end
28
22
  end
@@ -33,7 +33,7 @@ module Stretchy
33
33
  end
34
34
 
35
35
  def gateway(&block)
36
- @gateway ||= Stretchy::Repository.create(index_name: index_name, klass: base_class)
36
+ @gateway ||= Stretchy::Repository.create(client: Stretchy.configuration.client, index_name: index_name, klass: base_class)
37
37
  block.arity < 1 ? @gateway.instance_eval(&block) : block.call(@gateway) if block_given?
38
38
  @gateway
39
39
  end
@@ -0,0 +1,86 @@
1
+ module Stretchy
2
+ module OpenSearchCompatibility
3
+ extend ActiveSupport::Concern
4
+
5
+ # Patches the Elasticsearch::Persistence::Repository::Search module to remove the
6
+ # document type from the request for compatability with OpenSearch
7
+ def self.opensearch_patch!
8
+ patch = Module.new do
9
+ def search(query_or_definition, options={})
10
+ request = { index: index_name }
11
+
12
+ if query_or_definition.respond_to?(:to_hash)
13
+ request[:body] = query_or_definition.to_hash
14
+ elsif query_or_definition.is_a?(String)
15
+ request[:q] = query_or_definition
16
+ else
17
+ raise ArgumentError, "[!] Pass the search definition as a Hash-like object or pass the query as a String" +
18
+ " -- #{query_or_definition.class} given."
19
+ end
20
+
21
+ Elasticsearch::Persistence::Repository::Response::Results.new(self, client.search(request.merge(options)))
22
+ end
23
+
24
+ def count(query_or_definition=nil, options={})
25
+ query_or_definition ||= { query: { match_all: {} } }
26
+ request = { index: index_name}
27
+
28
+ if query_or_definition.respond_to?(:to_hash)
29
+ request[:body] = query_or_definition.to_hash
30
+ elsif query_or_definition.is_a?(String)
31
+ request[:q] = query_or_definition
32
+ else
33
+ raise ArgumentError, "[!] Pass the search definition as a Hash-like object or pass the query as a String" +
34
+ " -- #{query_or_definition.class} given."
35
+ end
36
+
37
+ client.count(request.merge(options))['count']
38
+ end
39
+ end
40
+
41
+ store = Module.new do
42
+ def save(document, options={})
43
+ serialized = serialize(document)
44
+ id = __get_id_from_document(serialized)
45
+ request = { index: index_name,
46
+ id: id,
47
+ body: serialized }
48
+ client.index(request.merge(options))
49
+ end
50
+
51
+
52
+ def update(document_or_id, options = {})
53
+ if document_or_id.is_a?(String) || document_or_id.is_a?(Integer)
54
+ id = document_or_id
55
+ body = options
56
+ else
57
+ document = serialize(document_or_id)
58
+ id = __extract_id_from_document(document)
59
+ if options[:script]
60
+ body = options
61
+ else
62
+ body = { doc: document }.merge(options)
63
+ end
64
+ end
65
+ client.update(index: index_name, id: id, body: body)
66
+ end
67
+
68
+ def delete(document_or_id, options = {})
69
+ if document_or_id.is_a?(String) || document_or_id.is_a?(Integer)
70
+ id = document_or_id
71
+ else
72
+ serialized = serialize(document_or_id)
73
+ id = __get_id_from_document(serialized)
74
+ end
75
+ client.delete({ index: index_name, id: id }.merge(options))
76
+ end
77
+ end
78
+
79
+
80
+ ::Elasticsearch::Persistence::Repository.send(:include, patch)
81
+ ::Elasticsearch::Persistence::Repository.send(:include, store)
82
+ end
83
+
84
+
85
+ end
86
+ end
@@ -6,20 +6,20 @@ module Stretchy
6
6
 
7
7
  VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
8
8
 
9
- def initialize
10
- @registry = Hash.new { |hash, key| hash[key] = {} }
11
- end
9
+ # def initialize
10
+ self.registry = Hash.new { |hash, key| hash[key] = {} }
11
+ # end
12
12
 
13
13
  # Obtains the value for a given +scope_name+ and +variable_name+.
14
- def value_for(scope_type, variable_name)
14
+ def self.value_for(scope_type, variable_name)
15
15
  raise_invalid_scope_type!(scope_type)
16
- @registry[scope_type][variable_name]
16
+ self.registry[scope_type][variable_name]
17
17
  end
18
18
 
19
19
  # Sets the +value+ for a given +scope_type+ and +variable_name+.
20
- def set_value_for(scope_type, variable_name, value)
20
+ def self.set_value_for(scope_type, variable_name, value)
21
21
  raise_invalid_scope_type!(scope_type)
22
- @registry[scope_type][variable_name] = value
22
+ self.registry[scope_type][variable_name] = value
23
23
  end
24
24
 
25
25
  private
@@ -13,13 +13,13 @@ module Stretchy
13
13
  end
14
14
 
15
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)
16
+ ScopeRegistry.new.registry[:current_scope][base_class.to_s]
17
+ # ScopeRegistry.value_for(:current_scope, base_class.to_s)
18
18
  end
19
19
 
20
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)
21
+ ScopeRegistry.new.registry[:current_scope][base_class.to_s] = scope
22
+ # ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
23
23
  end
24
24
  end
25
25
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stretchy
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/stretchy.rb CHANGED
@@ -3,10 +3,8 @@ require 'zeitwerk'
3
3
  require 'amazing_print'
4
4
  require 'rainbow'
5
5
  require 'jbuilder'
6
- # require 'elasticsearch/rails'
7
6
  require 'elasticsearch/model'
8
7
  require 'elasticsearch/persistence'
9
- # require 'active_support/concern'
10
8
  require 'active_model'
11
9
  require 'active_support/all'
12
10
  require 'active_model/type/array'
@@ -16,21 +14,57 @@ ActiveModel::Type.register(:array, ActiveModel::Type::Array)
16
14
  ActiveModel::Type.register(:hash, ActiveModel::Type::Hash)
17
15
 
18
16
  require_relative "stretchy/version"
19
- require_relative "stretchy/instrumentation/railtie" if defined?(Rails)
17
+ require_relative "rails/instrumentation/railtie" if defined?(Rails)
20
18
 
21
19
  module Stretchy
22
20
  module Errors
23
21
  class QueryOptionMissing < StandardError; end
24
22
  end
25
23
 
24
+ class Configuration
25
+
26
+ attr_accessor :client
27
+ attr_accessor :opensearch
28
+
29
+ def initialize
30
+ @client = Elasticsearch::Client.new url: 'http://localhost:9200'
31
+ @opensearch = false
32
+ end
33
+
34
+ def client=(client)
35
+ @client = client
36
+ self.opensearch = true if @client.class.name =~ /OpenSearch/
37
+ end
38
+
39
+ def opensearch=(bool)
40
+ @opensearch = bool
41
+ OpenSearchCompatibility.opensearch_patch! if bool
42
+ end
43
+
44
+ def opensearch?
45
+ @opensearch
46
+ end
47
+
48
+ end
49
+
26
50
  class << self
27
51
  def logger
28
52
  @logger ||= Logger.new(STDOUT)
29
53
  end
54
+
55
+ def configuration
56
+ @configuration ||= Configuration.new
57
+ end
58
+
59
+ def configure
60
+ yield configuration
61
+ end
30
62
  end
31
63
 
32
64
  end
33
65
 
66
+
67
+
34
68
  loader = Zeitwerk::Loader.new
35
69
  loader.tag = File.basename(__FILE__, ".rb")
36
70
  loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
@@ -0,0 +1 @@
1
+ require_relative "../../lib/stretchy"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stretchy-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Spencer Markowski
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-04 00:00:00.000000000 Z
11
+ date: 2024-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -28,42 +28,42 @@ dependencies:
28
28
  name: elasticsearch-rails
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '7.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '7.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: elasticsearch-persistence
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '7.1'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '7.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: elasticsearch-model
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '7.1'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '7.1'
69
69
  - !ruby/object:Gem::Dependency
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: 0.9.36
167
+ - !ruby/object:Gem::Dependency
168
+ name: opensearch-ruby
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '3.0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '3.0'
167
181
  description: Provides a familiar ActiveRecord-like interface for working with Elasticsearch
168
182
  email:
169
183
  - spencer.markowski@theablefew.com
@@ -197,6 +211,7 @@ files:
197
211
  - lib/stretchy/model/callbacks.rb
198
212
  - lib/stretchy/model/serialization.rb
199
213
  - lib/stretchy/null_relation.rb
214
+ - lib/stretchy/open_search_compatibility.rb
200
215
  - lib/stretchy/persistence.rb
201
216
  - lib/stretchy/querying.rb
202
217
  - lib/stretchy/record.rb
@@ -217,6 +232,7 @@ files:
217
232
  - lib/stretchy/utils.rb
218
233
  - lib/stretchy/version.rb
219
234
  - sig/stretchy.rbs
235
+ - stretchy-model/lib/stretchy-model.rb
220
236
  - stretchy.logo.png
221
237
  homepage: https://github.com/theablefew/stretchy
222
238
  licenses:
@@ -229,6 +245,7 @@ post_install_message:
229
245
  rdoc_options: []
230
246
  require_paths:
231
247
  - lib
248
+ - stretchy-model/lib
232
249
  required_ruby_version: !ruby/object:Gem::Requirement
233
250
  requirements:
234
251
  - - ">="