Skip to content

Commit

Permalink
chore: moved hooks to a separate project to be reused by esse-sequel …
Browse files Browse the repository at this point in the history
…plugin
  • Loading branch information
marcosgz committed Jan 21, 2025
1 parent 820af84 commit a1939ba
Show file tree
Hide file tree
Showing 11 changed files with 36 additions and 221 deletions.
11 changes: 7 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
PATH
remote: .
specs:
esse-active_record (0.3.8)
esse-active_record (0.3.9)
activerecord (>= 4.2, < 8)
esse (>= 0.3.0)
esse-hooks

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -39,9 +40,11 @@ GEM
elasticsearch-transport (7.17.10)
faraday (>= 1, < 3)
multi_json
esse (0.3.4)
esse (0.3.5)
multi_json
thor (>= 0.19)
esse-hooks (0.0.1)
esse (>= 0.3.0)
esse-rspec (0.0.6)
esse (>= 0.2.4)
rspec (>= 3)
Expand Down Expand Up @@ -122,7 +125,7 @@ GEM
standard-performance (1.0.1)
lint_roller (~> 1.0)
rubocop-performance (~> 1.16.0)
thor (1.3.1)
thor (1.3.2)
thread_safe (0.3.6)
tzinfo (1.2.11)
thread_safe (~> 0.1)
Expand Down Expand Up @@ -157,4 +160,4 @@ DEPENDENCIES
yard

BUNDLED WITH
2.3.22
2.3.26
7 changes: 5 additions & 2 deletions ci/Gemfile.rails-5.2.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
PATH
remote: ..
specs:
esse-active_record (0.3.8)
esse-active_record (0.3.9)
activerecord (>= 4.2, < 8)
esse (>= 0.3.0)
esse-hooks

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -45,6 +46,8 @@ GEM
esse (0.3.4)
multi_json
thor (>= 0.19)
esse-hooks (0.0.1)
esse (>= 0.3.0)
esse-rspec (0.0.6)
esse (>= 0.2.4)
rspec (>= 3)
Expand Down Expand Up @@ -156,4 +159,4 @@ DEPENDENCIES
yard

BUNDLED WITH
2.3.22
2.3.26
7 changes: 5 additions & 2 deletions ci/Gemfile.rails-6.0.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
PATH
remote: ..
specs:
esse-active_record (0.3.8)
esse-active_record (0.3.9)
activerecord (>= 4.2, < 8)
esse (>= 0.3.0)
esse-hooks

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -44,6 +45,8 @@ GEM
esse (0.3.4)
multi_json
thor (>= 0.19)
esse-hooks (0.0.1)
esse (>= 0.3.0)
esse-rspec (0.0.6)
esse (>= 0.2.4)
rspec (>= 3)
Expand Down Expand Up @@ -157,4 +160,4 @@ DEPENDENCIES
yard

BUNDLED WITH
2.3.22
2.3.26
7 changes: 5 additions & 2 deletions ci/Gemfile.rails-6.1.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
PATH
remote: ..
specs:
esse-active_record (0.3.8)
esse-active_record (0.3.9)
activerecord (>= 4.2, < 8)
esse (>= 0.3.0)
esse-hooks

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -44,6 +45,8 @@ GEM
esse (0.3.4)
multi_json
thor (>= 0.19)
esse-hooks (0.0.1)
esse (>= 0.3.0)
esse-rspec (0.0.6)
esse (>= 0.2.4)
rspec (>= 3)
Expand Down Expand Up @@ -157,4 +160,4 @@ DEPENDENCIES
yard

BUNDLED WITH
2.3.22
2.3.26
7 changes: 5 additions & 2 deletions ci/Gemfile.rails-7.0.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
PATH
remote: ..
specs:
esse-active_record (0.3.8)
esse-active_record (0.3.9)
activerecord (>= 4.2, < 8)
esse (>= 0.3.0)
esse-hooks

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -44,6 +45,8 @@ GEM
esse (0.3.4)
multi_json
thor (>= 0.19)
esse-hooks (0.0.1)
esse (>= 0.3.0)
esse-rspec (0.0.6)
esse (>= 0.2.4)
rspec (>= 3)
Expand Down Expand Up @@ -157,4 +160,4 @@ DEPENDENCIES
yard

BUNDLED WITH
2.3.22
2.3.26
7 changes: 5 additions & 2 deletions ci/Gemfile.rails-7.1.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
PATH
remote: ..
specs:
esse-active_record (0.3.8)
esse-active_record (0.3.9)
activerecord (>= 4.2, < 8)
esse (>= 0.3.0)
esse-hooks

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -51,6 +52,8 @@ GEM
esse (0.3.4)
multi_json
thor (>= 0.19)
esse-hooks (0.0.1)
esse (>= 0.3.0)
esse-rspec (0.0.6)
esse (>= 0.2.4)
rspec (>= 3)
Expand Down Expand Up @@ -165,4 +168,4 @@ DEPENDENCIES
yard

BUNDLED WITH
2.3.22
2.3.26
1 change: 1 addition & 0 deletions esse-active_record.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']

spec.add_dependency 'esse', '>= 0.3.0'
spec.add_dependency 'esse-hooks', '>= 0.0.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
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'esse'
require 'esse-hooks'
require 'active_record'
require_relative 'active_record/version'
require_relative 'active_record/callbacks'
Expand Down
201 changes: 1 addition & 200 deletions lib/esse/active_record/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,206 +5,7 @@ module ActiveRecord
module Hooks
STORE_STATE_KEY = :esse_active_record_hooks

class << self
def register_model(model_class)
@models ||= []
@models |= [model_class]
end

def models
@models || []
end

def model_names
models.map(&:to_s)
end

# Global enable indexing callbacks. If no repository is specified, all repositories will be enabled.
# @param repos [Array<String, Esse::Index, Esse::Repo>]
# @return [void]
def enable!(*repos)
filter_repositories(*repos).each do |repo|
state[:repos][repo] = true
end
end

# Global disable indexing callbacks. If no repository is specified, all repositories will be disabled.
# @param repos [Array<String, Esse::Index, Esse::Repo>]
# @return [void]
def disable!(*repos)
filter_repositories(*repos).each do |repo|
state[:repos][repo] = false
end
end

# Check if the given repository is enabled for indexing. If no repository is specified, all repositories will be checked.
#
# @param repos [Array<String, Esse::Index, Esse::Repo>]
# @return [Boolean]
def disabled?(*repos)
filter_repositories(*repos).all? { |repo| !state[:repos][repo] }
end

# Check if the given repository is enabled for indexing. If no repository is specified, all repositories will be checked.
#
# @param repos [Array<String, Esse::Index, Esse::Repo>]
# @return [Boolean]
def enabled?(*repos)
filter_repositories(*repos).all? { |repo| state[:repos][repo] }
end

# Enable model indexing callbacks for the given model. If no repository is specified, all repositories will be enabled.
#
# @param model_class [Class]
# @param repos [Array<String, Esse::Index, Esse::Repo>]
# @raise [ArgumentError] if model repository is not registered for the given model
# @return [void]
def enable_model!(model_class, *repos)
ensure_registered_model_class!(model_class)
filter_model_repositories(model_class, *repos).each do |repo|
state[:models][model_class][repo] = true
end
end

# Disable model indexing callbacks for the given model. If no repository is specified, all repositories will be disabled.
#
# @param model_class [Class]
# @param repos [Array<String, Esse::Index, Esse::Repo>]
# @raise [ArgumentError] if model repository is not registered for the given model
# @return [void]
def disable_model!(model_class, *repos)
ensure_registered_model_class!(model_class)
filter_model_repositories(model_class, *repos).each do |repo|
state[:models][model_class][repo] = false
end
end

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_callback' callback defined"
end

# Check if the given model is enabled for indexing. If no repository is specified, all repositories will be checked.
#
# @param model_class [Class]
# @param repos [Array<String, Esse::Index, Esse::Repo>]
# @return [Boolean]
def enabled_for_model?(model_class, *repos)
return false unless registered_model_class?(model_class)

filter_model_repositories(model_class, *repos).all? do |repo|
state.dig(:models, model_class, repo) != false
end
end

# Disable indexing callbacks execution for the block execution.
# Example:
# Esse::ActiveRecord::Hooks.without_indexing { User.create! }
# Esse::ActiveRecord::Hooks.without_indexing(UsersIndex, AccountsIndex.repo(:user)) { User.create! }
def without_indexing(*repos)
state_before_disable = state[:repos].dup
disable!(*repos)

yield
ensure
state[:repos] = state_before_disable
end

# Disable model indexing callbacks execution for the block execution for the given model.
# Example:
# BroadcastChanges.without_indexing_for_model(User) { }
# BroadcastChanges.without_indexing_for_model(User, :datasync, :other) { }
def without_indexing_for_model(model_class, *repos)
state_before_disable = state[:models].dig(model_class).dup
disable_model!(model_class, *repos)
yield
ensure
if state_before_disable.nil?
state[:models].delete(model_class)
else
state[:models][model_class] = state_before_disable
end
end

def resolve_index_repository(name)
index_name, repo_name = name.to_s.underscore.split('::').join('/').split(':', 2)
if index_name !~ /(I|_i)ndex$/ && index_name !~ /_index\/([\w_]+)$/
index_name = format('%<index_name>s_index', index_name: index_name)
end
klass = index_name.classify.constantize
return klass if klass <= Esse::Repository

repo_name.present? ? klass.repo(repo_name) : klass.repo
end

private

def all_repos
models.flat_map { |model_class| model_repos(model_class) }.uniq
end

# Returns a list of all repositories for the given model
# @return [Array<Symbol>]
def model_repos(model_class)
expand_index_repos(*model_class.esse_callbacks.keys)
end

# Returns a list of all repositories for the given model
# If no repository is specified, all repositories will be returned.
# @return [Array<*Esse::Repository>] List of repositories
def filter_repositories(*repos)
(expand_index_repos(*repos) & all_repos).presence || all_repos
end

# Return repositorys for the given model. If no repository is specified, all repositories will be returned.
#
# @param model_class [Class]
# @param repos [Array<*Esse::Repository>] List of repositories to check for the given model
# @return [Array<*Esse::Repository>] List of repositories
def filter_model_repositories(model_class, *repos)
model_repos = model_repos(model_class) & all_repos
(expand_index_repos(*repos) & model_repos).presence || model_repos
end

def expand_index_repos(*repos)
repos.flat_map do |repo_name|
case repo_name
when Class
(repo_name <= Esse::Index) ? repo_name.repo_hash.values : repo_name
when String, Symbol
resolve_index_repository(repo_name)
else
raise ArgumentError, "Invalid index or repository name: #{repo_name.inspect}"
end
end
end

# Check if model class is registered
# @return [Boolean] true if model class is registered
def registered_model_class?(model_class)
models.include?(model_class)
end

# Data Structure:
#
# repos: { <Esse::Repository class> => <true|false>, ... }
# models: {
# <ActiveRecord::Base class> => {
# <Esse::Repository class> => <true|false>
# }
# }
def state
global_store[STORE_STATE_KEY] ||= {
repos: all_repos.map { |k| [k, true] }.to_h, # Control global state of the index repository level
models: Hash.new { |h, k| h[k] = {} }, # Control the state of the model & index repository level
}
end

def global_store
Thread.current
end
end
include Esse::Hooks[store_key: STORE_STATE_KEY]
end
end
end
Loading

0 comments on commit a1939ba

Please sign in to comment.