Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy update attributes callback #7

Merged
merged 15 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

source 'https://rubygems.org'

gem 'esse', '~> 0.2.4'
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

gem 'esse', '~> 0.3.0'
gem 'sqlite3', '~> 1.7.3'
gem 'activerecord', '~> 5.2'
gem 'esse-rspec', '~> 0.0.6'
Expand Down
10 changes: 5 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
PATH
remote: .
specs:
esse-active_record (0.2.1)
esse-active_record (0.3.0)
activerecord (>= 4.2, < 8)
esse (>= 0.2.3)
esse (>= 0.3.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -39,7 +39,7 @@ GEM
elasticsearch-transport (7.17.10)
faraday (>= 1, < 3)
multi_json
esse (0.2.6)
esse (0.3.0)
multi_json
thor (>= 0.19)
esse-rspec (0.0.6)
Expand Down Expand Up @@ -122,7 +122,7 @@ GEM
standard-performance (1.0.1)
lint_roller (~> 1.0)
rubocop-performance (~> 1.16.0)
thor (1.3.0)
thor (1.3.1)
thread_safe (0.3.6)
tzinfo (1.2.11)
thread_safe (~> 0.1)
Expand All @@ -142,7 +142,7 @@ DEPENDENCIES
awesome_print
dotenv
elasticsearch (~> 7.17, >= 7.17.10)
esse (~> 0.2.4)
esse (~> 0.3.0)
esse-active_record!
esse-rspec (~> 0.0.6)
pry
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ end

### Indexing Callbacks

The `index_callbacks` callback can be used to automaitcally index or delete documents after commit on create/update/destroy events.
The `index_callback` callback can be used to automaitcally index or delete documents after commit on create/update/destroy events.

```ruby
class UsersIndex < Esse::Index
Expand All @@ -173,9 +173,9 @@ class User < ApplicationRecord
# Using a index and repository as argument. Note that the index name is used instead of the
# of the constant name. it's so because index and model depends on each other should result in
# circular dependencies issues.
index_callbacks 'users_index:user'
index_callback 'users_index:user'
# Using a block to direct a different object to be indexed
index_callbacks('organizations') { user.organization } # The `_index` suffix and repo name is optional on the index name
index_callback('organizations') { user.organization } # The `_index` suffix and repo name is optional on the index name
end
```

Expand All @@ -194,7 +194,7 @@ or by some specific list of index or index's repository
```ruby
Esse::ActiveRecord::Hooks.disable!(UsersIndex.repo)
Esse::ActiveRecord::Hooks.enable!(UsersIndex.repo)
Esse::ActiveRecord::Hooks.without_indexing(AccountsIndex UsersIndex.repo, ) do
Esse::ActiveRecord::Hooks.without_indexing(AccountsIndex, UsersIndex.repo) do
10.times { User.create! }
end
```
Expand Down
6 changes: 3 additions & 3 deletions ci/Gemfile.rails-5.2.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
PATH
remote: ..
specs:
esse-active_record (0.2.1)
esse-active_record (0.3.0)
activerecord (>= 4.2, < 8)
esse (>= 0.2.3)
esse (>= 0.3.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -39,7 +39,7 @@ GEM
elasticsearch-transport (7.17.10)
faraday (>= 1, < 3)
multi_json
esse (0.2.6)
esse (0.3.0)
multi_json
thor (>= 0.19)
esse-rspec (0.0.6)
Expand Down
6 changes: 3 additions & 3 deletions ci/Gemfile.rails-6.0.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
PATH
remote: ..
specs:
esse-active_record (0.2.1)
esse-active_record (0.3.0)
activerecord (>= 4.2, < 8)
esse (>= 0.2.3)
esse (>= 0.3.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -37,7 +37,7 @@ GEM
elasticsearch-transport (7.17.10)
faraday (>= 1, < 3)
multi_json
esse (0.2.6)
esse (0.3.0)
multi_json
thor (>= 0.19)
esse-rspec (0.0.6)
Expand Down
6 changes: 3 additions & 3 deletions ci/Gemfile.rails-6.1.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
PATH
remote: ..
specs:
esse-active_record (0.2.1)
esse-active_record (0.3.0)
activerecord (>= 4.2, < 8)
esse (>= 0.2.3)
esse (>= 0.3.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -37,7 +37,7 @@ GEM
elasticsearch-transport (7.17.10)
faraday (>= 1, < 3)
multi_json
esse (0.2.6)
esse (0.3.0)
multi_json
thor (>= 0.19)
esse-rspec (0.0.6)
Expand Down
6 changes: 3 additions & 3 deletions ci/Gemfile.rails-7.0.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
PATH
remote: ..
specs:
esse-active_record (0.2.1)
esse-active_record (0.3.0)
activerecord (>= 4.2, < 8)
esse (>= 0.2.3)
esse (>= 0.3.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -37,7 +37,7 @@ GEM
elasticsearch-transport (7.17.10)
faraday (>= 1, < 3)
multi_json
esse (0.2.6)
esse (0.3.0)
multi_json
thor (>= 0.19)
esse-rspec (0.0.6)
Expand Down
6 changes: 3 additions & 3 deletions ci/Gemfile.rails-7.1.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
PATH
remote: ..
specs:
esse-active_record (0.2.1)
esse-active_record (0.3.0)
activerecord (>= 4.2, < 8)
esse (>= 0.2.3)
esse (>= 0.3.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -47,7 +47,7 @@ GEM
elasticsearch-transport (7.17.10)
faraday (>= 1, < 3)
multi_json
esse (0.2.6)
esse (0.3.0)
multi_json
thor (>= 0.19)
esse-rspec (0.0.6)
Expand Down
2 changes: 1 addition & 1 deletion esse-active_record.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']

spec.add_dependency 'esse', '>= 0.2.3'
spec.add_dependency 'esse', '>= 0.3.0'
spec.add_dependency 'activerecord', '>= 4.2', '< 8'
spec.add_development_dependency 'awesome_print'
spec.add_development_dependency 'dotenv'
Expand Down
1 change: 1 addition & 0 deletions lib/esse/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'esse'
require 'active_record'
require_relative 'active_record/version'
require_relative 'active_record/callbacks'
require_relative 'active_record/model'
require_relative 'active_record/hooks'
require_relative 'active_record/collection'
Expand Down
64 changes: 64 additions & 0 deletions lib/esse/active_record/callbacks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# frozen_string_literal: true

module Esse
module ActiveRecord
class Callback
attr_reader :repo, :options, :block_result

def initialize(repo:, block_result: nil, **kwargs)
@repo = repo
@options = kwargs
@block_result = block_result
end

def call(model)
raise NotImplementedError, 'You must implement #call method'
end
end

module Callbacks
class << self
def to_h
@callbacks || {}.freeze
end

def register_callback(identifier, operation, callback_class)
unless callback_class < Esse::ActiveRecord::Callback
raise ArgumentError, 'callback_class must be a subclass of Esse::ActiveRecord::Callback'
end

key = :"#{identifier}_on_#{operation}"

@callbacks = @callbacks ? @callbacks.dup : {}
if @callbacks.key?(key)
raise ArgumentError, "callback #{identifier} for #{operation} operation already registered"
end

@callbacks[key] = callback_class
ensure
@callbacks&.freeze
end

def registered?(identifier, operation)
return false unless @callbacks

@callbacks.key?(:"#{identifier}_on_#{operation}")
end

def fetch!(identifier, operation)
key = :"#{identifier}_on_#{operation}"
if registered?(identifier, operation)
[key, @callbacks[key]]
else
raise ArgumentError, "callback #{identifier} for #{operation} operation not registered"
end
end
end
end
end
end

require_relative 'callbacks/indexing_on_create'
require_relative 'callbacks/indexing_on_update'
require_relative 'callbacks/indexing_on_destroy'
require_relative 'callbacks/update_lazy_attribute'
16 changes: 16 additions & 0 deletions lib/esse/active_record/callbacks/indexing_on_create.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module Esse::ActiveRecord
module Callbacks
class IndexingOnCreate < Callback
def call(model)
record = block_result || model
document = repo.serialize(record)
repo.index.index(document, **options) if document
true
end
end

register_callback(:indexing, :create, IndexingOnCreate)
end
end
18 changes: 18 additions & 0 deletions lib/esse/active_record/callbacks/indexing_on_destroy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Esse::ActiveRecord
module Callbacks
class IndexingOnDestroy < Callback
def call(model)
record = block_result || model
document = repo.serialize(record)
repo.index.delete(document, **options) if document
true
rescue Esse::Transport::NotFoundError
true
end
end

register_callback(:indexing, :destroy, IndexingOnDestroy)
end
end
34 changes: 34 additions & 0 deletions lib/esse/active_record/callbacks/indexing_on_update.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module Esse::ActiveRecord
module Callbacks
class IndexingOnUpdate < Callback
def call(model)
record = block_result || model

document = repo.serialize(record)
return true unless document

repo.index.index(document, **options)
return true unless document.routing

prev_record = model.class.new(model.attributes.merge(model.previous_changes.transform_values(&:first))).tap(&:readonly!)
prev_document = repo.serialize(prev_record)

return true unless prev_document
return true if [prev_document.id, prev_document.routing].include?(nil)
return true if prev_document.routing == document.routing
return true if prev_document.id != document.id

begin
repo.index.delete(prev_document, **options)
rescue Esse::Transport::NotFoundError
end

true
end
end

register_callback(:indexing, :update, IndexingOnUpdate)
end
end
27 changes: 27 additions & 0 deletions lib/esse/active_record/callbacks/update_lazy_attribute.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

module Esse::ActiveRecord
module Callbacks
class UpdateLazyAttribute < Callback
attr_reader :attribute_name

def initialize(attribute_name:, **kwargs, &block)
@attribute_name = attribute_name
super(**kwargs, &block)
end

def call(model)
related_ids = Array(block_result || model.id)
return true if related_ids.empty?

repo.update_documents_attribute(attribute_name, *related_ids, **options)

true
end
end

register_callback(:update_lazy_attribute, :create, UpdateLazyAttribute)
register_callback(:update_lazy_attribute, :update, UpdateLazyAttribute)
register_callback(:update_lazy_attribute, :destroy, UpdateLazyAttribute)
end
end
4 changes: 2 additions & 2 deletions lib/esse/active_record/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def disable_model!(model_class, *repos)
def ensure_registered_model_class!(model_class)
return if registered_model_class?(model_class)

raise ArgumentError, "Model class #{model_class} is not registered. The model should inherit from Esse::ActiveRecord::Model and have a `index_callbacks' callback defined"
raise ArgumentError, "Model class #{model_class} is not registered. The model should inherit from Esse::ActiveRecord::Model and have a `index_callback' callback defined"
end

# Check if the given model is enabled for indexing. If no repository is specified, all repositories will be checked.
Expand Down Expand Up @@ -147,7 +147,7 @@ def all_repos
# Returns a list of all repositories for the given model
# @return [Array<Symbol>]
def model_repos(model_class)
expand_index_repos(*model_class.esse_index_repos.keys)
expand_index_repos(*model_class.esse_callbacks.keys)
end

# Returns a list of all repositories for the given model
Expand Down
Loading