stretchy-model 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="