From 6606333dbfe673066509b5e3224c1cce15f83c3c Mon Sep 17 00:00:00 2001 From: "Marcos G. Zimmermann" Date: Tue, 21 Jan 2025 07:03:11 -0300 Subject: [PATCH] chore: moved hooks to a separate project to be reused by esse-sequel (#13) * chore: moved hooks to a separate project to be reused by esse-sequel plugin * chore: update changelog * fix: add libsqlite3 to ruby2 ci jobs --- .github/workflows/ruby.yml | 2 + CHANGELOG.md | 3 + Gemfile.lock | 11 +- ci/Gemfile.rails-5.2.lock | 7 +- ci/Gemfile.rails-6.0.lock | 7 +- ci/Gemfile.rails-6.1.lock | 7 +- ci/Gemfile.rails-7.0.lock | 7 +- ci/Gemfile.rails-7.1.lock | 7 +- esse-active_record.gemspec | 1 + lib/esse/active_record.rb | 1 + lib/esse/active_record/hooks.rb | 201 +----------------------------- lib/esse/active_record/version.rb | 2 +- sig/esse/active_record.rbs | 6 - 13 files changed, 41 insertions(+), 221 deletions(-) delete mode 100644 sig/esse/active_record.rbs diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index f773bf6..85b4ce9 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -34,6 +34,8 @@ jobs: BUNDLE_GEMFILE: ${{ matrix.gemfile }} steps: - uses: actions/checkout@v4 + - name: Install sqlite3 + run: sudo apt-get install -y libsqlite3-dev - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 73fa38d..6466583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.3.9 - 2025-01-21 +* Move hooks to a separate gem, [esse-hooks](https://github.com/marcosgz/esse-hooks) + ## 0.3.8 - 2024-08-08 * Add `connect_with:` option to the collection definition. * Adjust UpdateLazyAttribute callback to not use keyword arguments for better compatibility with older versions of Ruby. diff --git a/Gemfile.lock b/Gemfile.lock index 1718357..bd8ae75 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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/ @@ -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) @@ -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) @@ -157,4 +160,4 @@ DEPENDENCIES yard BUNDLED WITH - 2.3.22 + 2.3.26 diff --git a/ci/Gemfile.rails-5.2.lock b/ci/Gemfile.rails-5.2.lock index 33721ff..a31d075 100644 --- a/ci/Gemfile.rails-5.2.lock +++ b/ci/Gemfile.rails-5.2.lock @@ -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/ @@ -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) @@ -156,4 +159,4 @@ DEPENDENCIES yard BUNDLED WITH - 2.3.22 + 2.3.26 diff --git a/ci/Gemfile.rails-6.0.lock b/ci/Gemfile.rails-6.0.lock index 30aece2..0ce6ded 100644 --- a/ci/Gemfile.rails-6.0.lock +++ b/ci/Gemfile.rails-6.0.lock @@ -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/ @@ -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) @@ -157,4 +160,4 @@ DEPENDENCIES yard BUNDLED WITH - 2.3.22 + 2.3.26 diff --git a/ci/Gemfile.rails-6.1.lock b/ci/Gemfile.rails-6.1.lock index 8b5e138..d03acaa 100644 --- a/ci/Gemfile.rails-6.1.lock +++ b/ci/Gemfile.rails-6.1.lock @@ -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/ @@ -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) @@ -157,4 +160,4 @@ DEPENDENCIES yard BUNDLED WITH - 2.3.22 + 2.3.26 diff --git a/ci/Gemfile.rails-7.0.lock b/ci/Gemfile.rails-7.0.lock index 30aece2..0ce6ded 100644 --- a/ci/Gemfile.rails-7.0.lock +++ b/ci/Gemfile.rails-7.0.lock @@ -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/ @@ -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) @@ -157,4 +160,4 @@ DEPENDENCIES yard BUNDLED WITH - 2.3.22 + 2.3.26 diff --git a/ci/Gemfile.rails-7.1.lock b/ci/Gemfile.rails-7.1.lock index d6ba719..65010c7 100644 --- a/ci/Gemfile.rails-7.1.lock +++ b/ci/Gemfile.rails-7.1.lock @@ -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/ @@ -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) @@ -165,4 +168,4 @@ DEPENDENCIES yard BUNDLED WITH - 2.3.22 + 2.3.26 diff --git a/esse-active_record.gemspec b/esse-active_record.gemspec index 8eeb045..b996942 100644 --- a/esse-active_record.gemspec +++ b/esse-active_record.gemspec @@ -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' diff --git a/lib/esse/active_record.rb b/lib/esse/active_record.rb index 60288f2..9297fef 100644 --- a/lib/esse/active_record.rb +++ b/lib/esse/active_record.rb @@ -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' diff --git a/lib/esse/active_record/hooks.rb b/lib/esse/active_record/hooks.rb index 7f9fb58..f338eb6 100644 --- a/lib/esse/active_record/hooks.rb +++ b/lib/esse/active_record/hooks.rb @@ -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] - # @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] - # @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] - # @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] - # @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] - # @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] - # @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] - # @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('%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] - 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: { => , ... } - # models: { - # => { - # => - # } - # } - 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 diff --git a/lib/esse/active_record/version.rb b/lib/esse/active_record/version.rb index 4c9266a..fc583f8 100644 --- a/lib/esse/active_record/version.rb +++ b/lib/esse/active_record/version.rb @@ -2,6 +2,6 @@ module Esse module ActiveRecord - VERSION = '0.3.8' + VERSION = '0.3.9' end end diff --git a/sig/esse/active_record.rbs b/sig/esse/active_record.rbs deleted file mode 100644 index 1de66e4..0000000 --- a/sig/esse/active_record.rbs +++ /dev/null @@ -1,6 +0,0 @@ -module Esse - module ActiveRecord - VERSION: String - # See the writing guide of rbs: https://github.com/ruby/rbs#guides - end -end