cache_sweeper 0.1.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.
@@ -0,0 +1,104 @@
1
+ # lib/cache_sweeper/logger.rb
2
+ module CacheSweeper
3
+ class Logger
4
+ class << self
5
+ def log(level, message, context = {})
6
+ return unless should_log?(level)
7
+
8
+ formatted_message = format_message(message, level, context)
9
+ CacheSweeper.logger.send(level, formatted_message)
10
+ end
11
+
12
+ def debug(message, context = {})
13
+ log(:debug, message, context)
14
+ end
15
+
16
+ def info(message, context = {})
17
+ log(:info, message, context)
18
+ end
19
+
20
+ def warn(message, context = {})
21
+ log(:warn, message, context)
22
+ end
23
+
24
+ def error(message, context = {})
25
+ log(:error, message, context)
26
+ end
27
+
28
+ def log_initialization(message, context = {})
29
+ info("Initialization: #{message}", context)
30
+ end
31
+
32
+ def log_rule_execution(rule, record, result, context = {})
33
+ sweeper_name = rule[:sweeper_class]&.name || 'Unknown'
34
+ model_name = record.class.name
35
+ record_id = record.respond_to?(:id) ? record.id : 'unknown'
36
+
37
+ message = "Rule execution: #{sweeper_name} -> #{model_name}##{record_id}"
38
+ context.merge!({
39
+ sweeper: sweeper_name,
40
+ model: model_name,
41
+ record_id: record_id,
42
+ association: rule[:association],
43
+ attributes: rule[:attributes],
44
+ condition_result: result,
45
+ batching_mode: rule[:batching_mode],
46
+ async: rule[:async]
47
+ })
48
+
49
+ debug(message, context)
50
+ end
51
+
52
+ def log_performance(operation, duration, context = {})
53
+ message = "Performance: #{operation} took #{duration.round(3)}ms"
54
+ context.merge!({
55
+ operation: operation,
56
+ duration_ms: duration.round(3)
57
+ })
58
+
59
+ debug(message, context)
60
+ end
61
+
62
+ def log_error(error, context = {})
63
+ message = "Error: #{error.class.name}: #{error.message}"
64
+ context.merge!({
65
+ error_class: error.class.name,
66
+ error_message: error.message,
67
+ backtrace: error.backtrace&.first(5)
68
+ })
69
+
70
+ error(message, context)
71
+ end
72
+
73
+ def log_cache_operations(message, level = :info, context = {})
74
+ log(level, message, context)
75
+ end
76
+
77
+ def log_async_jobs(message, level = :info, context = {})
78
+ log(level, message, context)
79
+ end
80
+
81
+ def log_middleware(message, level = :info, context = {})
82
+ log(level, message, context)
83
+ end
84
+
85
+ private
86
+
87
+ def should_log?(level)
88
+ return false unless CacheSweeper.logger
89
+
90
+ level_priority = { debug: 0, info: 1, warn: 2, error: 3 }
91
+ current_level_priority = level_priority[CacheSweeper.log_level] || 1
92
+ requested_level_priority = level_priority[level] || 1
93
+
94
+ requested_level_priority >= current_level_priority
95
+ end
96
+
97
+ def format_message(message, level, context)
98
+ timestamp = Time.current.strftime("%Y-%m-%d %H:%M:%S.%3N")
99
+ context_str = context.any? ? " #{context.inspect}" : ""
100
+ "[CacheSweeper] [#{timestamp}] [#{level.to_s.upcase}] #{message}#{context_str}"
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,13 @@
1
+ module CacheSweeper
2
+ class Railtie < Rails::Railtie
3
+ initializer "cache_sweeper.configure_defaults" do
4
+ CacheSweeper.configure_defaults
5
+ end
6
+
7
+ config.after_initialize do
8
+ CacheSweeper.validate_configuration!
9
+ CacheSweeper::Loader.load_sweepers!
10
+ CacheSweeper::Loader.hook_sweepers!
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module CacheSweeper
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,108 @@
1
+ require "cache_sweeper/version"
2
+ require "cache_sweeper/dsl"
3
+ require "cache_sweeper/base"
4
+ require "cache_sweeper/logger"
5
+ require "cache_sweeper/loader"
6
+ require "cache_sweeper/async_worker"
7
+ require "cache_sweeper/flush_middleware"
8
+
9
+ module CacheSweeper
10
+ class Error < StandardError; end
11
+
12
+ DEFAULTS = {
13
+ log_level: :info,
14
+ trigger: :instant,
15
+ mode: :inline,
16
+ queue: :default,
17
+ sidekiq_options: {},
18
+ delete_multi_batch_size: 100
19
+ }.freeze
20
+
21
+ class << self
22
+ attr_accessor :logger, :log_level, :trigger, :mode, :queue, :sidekiq_options, :delete_multi_batch_size
23
+
24
+ def configure
25
+ yield self
26
+ end
27
+
28
+ def configure_defaults
29
+ DEFAULTS.each { |k, v| instance_variable_set("@#{k}", v) }
30
+
31
+ if defined?(Rails)
32
+ @log_level =
33
+ if Rails.env.development?
34
+ :debug
35
+ elsif Rails.env.production?
36
+ :warn
37
+ else
38
+ :info
39
+ end
40
+ end
41
+ end
42
+
43
+ def log_level=(level)
44
+ valid_levels = %i[debug info warn error]
45
+ unless valid_levels.include?(level)
46
+ raise ArgumentError,
47
+ "Invalid log level: #{level}. Must be one of: #{valid_levels.join(', ')}"
48
+ end
49
+ @log_level = level
50
+ end
51
+
52
+ def attached_sweepers
53
+ @attached_sweepers ||= []
54
+ end
55
+
56
+ def validate_configuration!
57
+ validate_async_mode(@mode, "global configuration")
58
+ end
59
+
60
+ def validate_async_mode(mode, context)
61
+ if mode == :async && !defined?(Sidekiq)
62
+ warn "CacheSweeper Warning: #{context} has mode set to :async but Sidekiq is not available. " \
63
+ "Async jobs will be executed synchronously. " \
64
+ "Add 'gem \"sidekiq\"' to your Gemfile to enable async processing."
65
+ end
66
+ end
67
+
68
+ def delete_cache_keys(keys, context = {})
69
+ keys_array = Array(keys)
70
+ return 0 if keys_array.empty?
71
+
72
+ batch_size = @delete_multi_batch_size || 100
73
+ deleted_count = 0
74
+ failed_count = 0
75
+
76
+ keys_array.each_slice(batch_size) do |batch|
77
+ begin
78
+ Rails.cache.delete_multi(batch)
79
+ deleted_count += batch.length
80
+ CacheSweeper::Logger.log_cache_operations("Deleted batch of #{batch.length} keys", :debug, context.merge({
81
+ batch_size: batch.length,
82
+ keys: batch
83
+ }))
84
+ rescue => e
85
+ failed_count += batch.length
86
+ CacheSweeper::Logger.log_error(e, context.merge({
87
+ batch_size: batch.length,
88
+ keys: batch,
89
+ error_type: 'delete_multi_error'
90
+ }))
91
+ end
92
+ end
93
+
94
+ CacheSweeper::Logger.log_cache_operations("Batch deletion completed", :info, context.merge({
95
+ total_keys: keys_array.length,
96
+ deleted_count: deleted_count,
97
+ failed_count: failed_count,
98
+ batch_size: batch_size
99
+ }))
100
+
101
+ deleted_count
102
+ end
103
+ end
104
+ end
105
+
106
+ if defined?(Rails)
107
+ require "cache_sweeper/railtie"
108
+ end
@@ -0,0 +1,4 @@
1
+ require 'rake'
2
+
3
+ namespace :cache_sweeper do
4
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cache_sweeper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rafay Qayyum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-09-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: request_store
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '8.0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '6.0'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '8.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: sidekiq
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '6.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '6.0'
61
+ description:
62
+ email: rafayqayyum786@gmail.com
63
+ executables: []
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - CODE_OF_CONDUCT.md
68
+ - LICENSE.txt
69
+ - README.md
70
+ - lib/cache_sweeper.rb
71
+ - lib/cache_sweeper/async_worker.rb
72
+ - lib/cache_sweeper/base.rb
73
+ - lib/cache_sweeper/dsl.rb
74
+ - lib/cache_sweeper/flush_middleware.rb
75
+ - lib/cache_sweeper/loader.rb
76
+ - lib/cache_sweeper/logger.rb
77
+ - lib/cache_sweeper/railtie.rb
78
+ - lib/cache_sweeper/version.rb
79
+ - lib/tasks/cache_sweeper.rake
80
+ homepage: https://github.com/rafayqayyum/cache_sweeper
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 2.6.0
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubygems_version: 3.2.33
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Flexible, rule-based cache invalidation for Rails with batching, async jobs,
103
+ and association support.
104
+ test_files: []