reactive-actions 0.1.0.pre.alpha.1 → 0.1.0.pre.alpha.3
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.
- checksums.yaml +4 -4
- data/README.md +1156 -324
- data/app/assets/javascripts/reactive_actions.js +271 -13
- data/app/controllers/reactive_actions/reactive_actions_controller.rb +5 -1
- data/lib/generators/reactive_actions/install/install_generator.rb +201 -35
- data/lib/generators/reactive_actions/install/templates/README +96 -20
- data/lib/generators/reactive_actions/install/templates/initializer.rb +59 -0
- data/lib/reactive_actions/concerns/rate_limiter.rb +174 -0
- data/lib/reactive_actions/concerns/security_checks.rb +108 -0
- data/lib/reactive_actions/configuration.rb +23 -3
- data/lib/reactive_actions/controller/rate_limiter.rb +187 -0
- data/lib/reactive_actions/errors.rb +17 -0
- data/lib/reactive_actions/rate_limiter.rb +165 -0
- data/lib/reactive_actions/reactive_action.rb +5 -0
- data/lib/reactive_actions/version.rb +1 -1
- data/lib/reactive_actions.rb +5 -1
- metadata +6 -2
@@ -2,31 +2,107 @@
|
|
2
2
|
|
3
3
|
ReactiveActions has been installed successfully!
|
4
4
|
|
5
|
-
|
5
|
+
SETUP COMPLETED:
|
6
|
+
================
|
7
|
+
|
8
|
+
✓ Routes added to config/routes.rb:
|
6
9
|
mount ReactiveActions::Engine, at: '/reactive_actions'
|
7
10
|
|
8
|
-
|
9
|
-
|
11
|
+
✓ JavaScript client configured with automatic initialization
|
12
|
+
- Added to config/importmap.rb
|
13
|
+
- Initialized in app/javascript/application.js
|
14
|
+
- Available globally as window.ReactiveActions
|
10
15
|
|
11
|
-
|
16
|
+
✓ Sample action created:
|
12
17
|
app/reactive_actions/example_action.rb
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
19
|
+
QUICK START:
|
20
|
+
============
|
21
|
+
|
22
|
+
1. Test the API endpoint:
|
23
|
+
curl -X POST http://localhost:3000/reactive_actions/execute \
|
24
|
+
-H "Content-Type: application/json" \
|
25
|
+
-d '{"action_name": "example", "action_params": {"name": "YourName"}}'
|
26
|
+
|
27
|
+
2. Use JavaScript client:
|
28
|
+
ReactiveActions.execute('example', { name: 'YourName' })
|
29
|
+
.then(response => console.log(response))
|
30
|
+
|
31
|
+
3. Use DOM binding (no JavaScript required):
|
32
|
+
<button reactive-action="click->example"
|
33
|
+
reactive-action-name="YourName">
|
34
|
+
Test Action
|
35
|
+
</button>
|
36
|
+
|
37
|
+
JAVASCRIPT CLIENT:
|
38
|
+
==================
|
39
|
+
|
40
|
+
The ReactiveActions client is automatically initialized and available as:
|
41
|
+
- window.ReactiveActions (global)
|
42
|
+
- Supports all HTTP methods: get, post, put, patch, delete
|
43
|
+
- Automatic CSRF token handling
|
44
|
+
- DOM binding with mutation observer
|
45
|
+
|
46
|
+
Examples:
|
47
|
+
// Execute actions
|
48
|
+
ReactiveActions.execute('action_name', { param: 'value' })
|
49
|
+
ReactiveActions.post('create_user', { name: 'John' })
|
50
|
+
ReactiveActions.get('fetch_data', { id: 123 })
|
51
|
+
|
52
|
+
// Configuration (if needed)
|
53
|
+
ReactiveActions.configure({
|
54
|
+
baseUrl: '/custom/path/execute',
|
55
|
+
defaultHttpMethod: 'PUT'
|
56
|
+
}).reinitialize()
|
57
|
+
|
58
|
+
DOM BINDING:
|
59
|
+
============
|
60
|
+
|
61
|
+
Add reactive-action attributes to any element:
|
62
|
+
|
63
|
+
<!-- Basic actions -->
|
64
|
+
<button reactive-action="click->update_user">Update</button>
|
65
|
+
<input reactive-action="change->search" type="text">
|
66
|
+
<form reactive-action="submit->create_post">...</form>
|
67
|
+
|
68
|
+
<!-- With HTTP methods -->
|
69
|
+
<button reactive-action="click->put#update_user">Update (PUT)</button>
|
70
|
+
<button reactive-action="click->delete#remove_user">Delete</button>
|
71
|
+
|
72
|
+
<!-- Pass data via attributes -->
|
73
|
+
<button reactive-action="click->update_user"
|
74
|
+
reactive-action-user-id="123"
|
75
|
+
reactive-action-name="John">
|
76
|
+
Update User
|
77
|
+
</button>
|
78
|
+
|
79
|
+
CREATING ACTIONS:
|
80
|
+
=================
|
81
|
+
|
82
|
+
Add files to app/reactive_actions/:
|
83
|
+
|
84
|
+
# app/reactive_actions/update_user_action.rb
|
85
|
+
class UpdateUserAction < ReactiveActions::ReactiveAction
|
86
|
+
def action
|
87
|
+
user = User.find(action_params[:user_id])
|
88
|
+
user.update(name: action_params[:name])
|
89
|
+
@result = { success: true, user: user.as_json }
|
90
|
+
end
|
91
|
+
|
92
|
+
def response
|
93
|
+
render json: @result
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
DOCUMENTATION & SUPPORT:
|
98
|
+
========================
|
99
|
+
|
100
|
+
For complete documentation and examples:
|
30
101
|
https://github.com/IstvanMs/reactive-actions
|
31
102
|
|
103
|
+
For troubleshooting and configuration options:
|
104
|
+
Check config/initializers/reactive_actions.rb
|
105
|
+
|
106
|
+
Happy coding with ReactiveActions! 🚀
|
107
|
+
|
32
108
|
===============================================================================
|
@@ -7,7 +7,66 @@ ReactiveActions.configure do |config|
|
|
7
7
|
|
8
8
|
# Configure instance variables to delegate from the controller to action classes
|
9
9
|
# config.delegated_instance_variables += [:custom_variable]
|
10
|
+
|
11
|
+
# Rate Limiting Configuration
|
12
|
+
# ============================
|
13
|
+
|
14
|
+
<% if rate_limiting_config[:rate_limiting_enabled] -%>
|
15
|
+
# Rate limiting is enabled
|
16
|
+
config.rate_limiting_enabled = true
|
17
|
+
|
18
|
+
<% if rate_limiting_config[:global_rate_limiting_enabled] -%>
|
19
|
+
# Global controller-level rate limiting is enabled
|
20
|
+
config.global_rate_limiting_enabled = true
|
21
|
+
config.global_rate_limit = <%= rate_limiting_config[:global_rate_limit] || 600 %>
|
22
|
+
config.global_rate_limit_window = <%= rate_limiting_config[:global_rate_limit_window] || '1.minute' %>
|
23
|
+
|
24
|
+
<% else -%>
|
25
|
+
# Global rate limiting is disabled
|
26
|
+
config.global_rate_limiting_enabled = false
|
27
|
+
|
28
|
+
<% end -%>
|
29
|
+
<% if rate_limiting_config[:custom_key_generator] -%>
|
30
|
+
# Custom rate limit key generator
|
31
|
+
# Uncomment and customize as needed:
|
32
|
+
# config.rate_limit_key_generator = ->(request, action_name) do
|
33
|
+
# # Example: user-based key with action scope
|
34
|
+
# user_id = request.headers['X-User-ID'] || 'anonymous'
|
35
|
+
# "#{action_name}:user:#{user_id}"
|
36
|
+
# end
|
37
|
+
|
38
|
+
<% end -%>
|
39
|
+
<% else -%>
|
40
|
+
# Rate limiting is disabled by default
|
41
|
+
# To enable rate limiting, uncomment and configure:
|
42
|
+
# config.rate_limiting_enabled = true
|
43
|
+
# config.global_rate_limiting_enabled = true
|
44
|
+
# config.global_rate_limit = 600
|
45
|
+
# config.global_rate_limit_window = 1.minute
|
46
|
+
|
47
|
+
# Custom rate limit key generator (optional)
|
48
|
+
# config.rate_limit_key_generator = ->(request, action_name) do
|
49
|
+
# # Example: user-based key with action scope
|
50
|
+
# user_id = request.headers['X-User-ID'] || 'anonymous'
|
51
|
+
# "#{action_name}:user:#{user_id}"
|
52
|
+
# end
|
53
|
+
|
54
|
+
<% end -%>
|
10
55
|
end
|
11
56
|
|
12
57
|
# Set the logger for ReactiveActions
|
13
58
|
ReactiveActions.logger = Rails.logger
|
59
|
+
|
60
|
+
# JavaScript Client Configuration
|
61
|
+
# ================================
|
62
|
+
# The JavaScript client is automatically initialized in application.js
|
63
|
+
# You can reconfigure it at runtime if needed:
|
64
|
+
#
|
65
|
+
# ReactiveActions.configure({
|
66
|
+
# baseUrl: '/custom/path/execute',
|
67
|
+
# enableAutoBinding: true,
|
68
|
+
# enableMutationObserver: true,
|
69
|
+
# defaultHttpMethod: 'POST'
|
70
|
+
# }).reinitialize();
|
71
|
+
#
|
72
|
+
# Available globally as window.ReactiveActions
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReactiveActions
|
4
|
+
module Concerns
|
5
|
+
# Rate limiting module for ReactiveActions that can be included in actions or other classes
|
6
|
+
# Provides convenient methods for checking and managing rate limits
|
7
|
+
module RateLimiter
|
8
|
+
# Helper method for rate limiting in security checks or actions
|
9
|
+
# Automatically respects the global rate_limiting_enabled setting
|
10
|
+
# @param key [String, nil] Custom cache key, uses default if nil
|
11
|
+
# @param limit [Integer] Maximum number of requests allowed
|
12
|
+
# @param window [ActiveSupport::Duration] Time window for the limit
|
13
|
+
# @param cost [Integer] Cost of this request (default: 1)
|
14
|
+
# @raise [RateLimitExceededError] When rate limit is exceeded
|
15
|
+
def rate_limit!(key: nil, limit: 100, window: 1.hour, cost: 1)
|
16
|
+
# Early return if rate limiting is disabled
|
17
|
+
return unless ReactiveActions.configuration.rate_limiting_enabled
|
18
|
+
|
19
|
+
effective_key = key || default_rate_limit_key
|
20
|
+
|
21
|
+
ReactiveActions::RateLimiter.check!(
|
22
|
+
key: effective_key,
|
23
|
+
limit: limit,
|
24
|
+
window: window,
|
25
|
+
cost: cost
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Check rate limit without raising an error (also respects configuration)
|
30
|
+
# @param key [String, nil] Custom cache key, uses default if nil
|
31
|
+
# @param limit [Integer] Maximum number of requests allowed
|
32
|
+
# @param window [ActiveSupport::Duration] Time window for the limit
|
33
|
+
# @return [Hash] Rate limit status information
|
34
|
+
def rate_limit_status(key: nil, limit: 100, window: 1.hour)
|
35
|
+
# Return "unlimited" status if rate limiting is disabled
|
36
|
+
unless ReactiveActions.configuration.rate_limiting_enabled
|
37
|
+
return {
|
38
|
+
limit: Float::INFINITY,
|
39
|
+
remaining: Float::INFINITY,
|
40
|
+
current: 0,
|
41
|
+
window: window,
|
42
|
+
exceeded: false,
|
43
|
+
enabled: false
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
effective_key = key || default_rate_limit_key
|
48
|
+
|
49
|
+
ReactiveActions::RateLimiter.check(
|
50
|
+
key: effective_key,
|
51
|
+
limit: limit,
|
52
|
+
window: window
|
53
|
+
).merge(enabled: true)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Reset rate limit for a specific key
|
57
|
+
# @param key [String, nil] Custom cache key, uses default if nil
|
58
|
+
# @param window [ActiveSupport::Duration] Time window for the limit
|
59
|
+
def reset_rate_limit!(key: nil, window: 1.hour)
|
60
|
+
return unless ReactiveActions.configuration.rate_limiting_enabled
|
61
|
+
|
62
|
+
effective_key = key || default_rate_limit_key
|
63
|
+
|
64
|
+
ReactiveActions::RateLimiter.reset!(
|
65
|
+
key: effective_key,
|
66
|
+
window: window
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Check if rate limiting is currently enabled
|
71
|
+
# @return [Boolean] True if rate limiting is enabled
|
72
|
+
def rate_limiting_enabled?
|
73
|
+
ReactiveActions.configuration.rate_limiting_enabled
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get remaining requests for a specific key without consuming any
|
77
|
+
# @param key [String, nil] Custom cache key, uses default if nil
|
78
|
+
# @param limit [Integer] Maximum number of requests allowed
|
79
|
+
# @param window [ActiveSupport::Duration] Time window for the limit
|
80
|
+
# @return [Integer, Float] Number of remaining requests, or Float::INFINITY if disabled
|
81
|
+
def rate_limit_remaining(key: nil, limit: 100, window: 1.hour)
|
82
|
+
status = rate_limit_status(key: key, limit: limit, window: window)
|
83
|
+
status[:remaining]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Check if a key would exceed rate limit for a given cost
|
87
|
+
# @param key [String, nil] Custom cache key, uses default if nil
|
88
|
+
# @param limit [Integer] Maximum number of requests allowed
|
89
|
+
# @param window [ActiveSupport::Duration] Time window for the limit
|
90
|
+
# @param cost [Integer] Cost to check (default: 1)
|
91
|
+
# @return [Boolean] True if the cost would exceed the limit
|
92
|
+
def rate_limit_would_exceed?(key: nil, limit: 100, window: 1.hour, cost: 1)
|
93
|
+
return false unless ReactiveActions.configuration.rate_limiting_enabled
|
94
|
+
|
95
|
+
status = rate_limit_status(key: key, limit: limit, window: window)
|
96
|
+
status[:remaining] < cost
|
97
|
+
end
|
98
|
+
|
99
|
+
# Create a rate limit key for a specific scope
|
100
|
+
# @param scope [String, Symbol] The scope (e.g., 'api', 'search', 'upload')
|
101
|
+
# @param identifier [String, nil] Custom identifier, uses default if nil
|
102
|
+
# @return [String] Formatted rate limit key
|
103
|
+
def rate_limit_key_for(scope, identifier: nil)
|
104
|
+
base_key = identifier || extract_identifier_from_context
|
105
|
+
"#{scope}:#{base_key}"
|
106
|
+
end
|
107
|
+
|
108
|
+
# Log rate limiting events for debugging/monitoring
|
109
|
+
# @param event [String] Event type (e.g., 'exceeded', 'checked', 'reset')
|
110
|
+
# @param details [Hash] Additional details to log
|
111
|
+
def log_rate_limit_event(event, details = {})
|
112
|
+
return unless ReactiveActions.logger
|
113
|
+
|
114
|
+
ReactiveActions.logger.info(
|
115
|
+
"Rate Limit #{event.capitalize}: #{details.merge(key: default_rate_limit_key)}"
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# Default key generation for rate limiting
|
122
|
+
# This method tries different approaches to generate a meaningful key
|
123
|
+
# @return [String] A rate limit key
|
124
|
+
def default_rate_limit_key
|
125
|
+
# Try different methods to get a meaningful identifier
|
126
|
+
if respond_to?(:current_user) && current_user
|
127
|
+
"user:#{current_user.id}"
|
128
|
+
elsif respond_to?(:controller) && controller.respond_to?(:request)
|
129
|
+
extract_key_from_request(controller.request)
|
130
|
+
elsif respond_to?(:request) && request
|
131
|
+
extract_key_from_request(request)
|
132
|
+
else
|
133
|
+
"anonymous:#{SecureRandom.hex(8)}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Extract identifier from current context (user, request, etc.)
|
138
|
+
# @return [String] An identifier for the current context
|
139
|
+
def extract_identifier_from_context
|
140
|
+
if respond_to?(:current_user) && current_user
|
141
|
+
current_user.id.to_s
|
142
|
+
elsif respond_to?(:controller) && controller.respond_to?(:request)
|
143
|
+
controller.request.remote_ip
|
144
|
+
elsif respond_to?(:request) && request
|
145
|
+
request.remote_ip
|
146
|
+
else
|
147
|
+
'unknown'
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Extract a rate limiting key from a request object
|
152
|
+
# @param request [ActionDispatch::Request] The request object
|
153
|
+
# @return [String] A rate limit key based on the request
|
154
|
+
def extract_key_from_request(request)
|
155
|
+
# Try to get user info from headers first
|
156
|
+
if request.headers['X-User-ID'].present?
|
157
|
+
"user:#{request.headers['X-User-ID']}"
|
158
|
+
elsif request.headers['Authorization'].present?
|
159
|
+
# Extract from API token/key
|
160
|
+
auth_header = request.headers['Authorization']
|
161
|
+
if auth_header.start_with?('Bearer ')
|
162
|
+
token = auth_header.gsub('Bearer ', '')
|
163
|
+
"token:#{Digest::SHA256.hexdigest(token)[0..8]}" # Hash for privacy
|
164
|
+
else
|
165
|
+
"auth:#{request.remote_ip}"
|
166
|
+
end
|
167
|
+
else
|
168
|
+
# Fallback to IP address
|
169
|
+
"ip:#{request.remote_ip}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReactiveActions
|
4
|
+
module Concerns
|
5
|
+
# Security checks module that provides simple security filtering with function names or lambdas
|
6
|
+
module SecurityChecks
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :security_filters, default: []
|
11
|
+
end
|
12
|
+
|
13
|
+
class_methods do
|
14
|
+
# Add a security check that runs before the action
|
15
|
+
#
|
16
|
+
# @param check [Symbol, Proc] The method name or lambda to execute
|
17
|
+
# @param options [Hash] Conditions for when to run the check
|
18
|
+
# @option options [Symbol, Array<Symbol>] :only Run only for these actions
|
19
|
+
# @option options [Symbol, Array<Symbol>] :except Skip for these actions
|
20
|
+
# @option options [Symbol, Proc] :if Run only if condition is true
|
21
|
+
# @option options [Symbol, Proc] :unless Skip if condition is true
|
22
|
+
def security_check(check, **options)
|
23
|
+
self.security_filters = security_filters + [{ check: check, **options }]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Skip all security checks for this action
|
27
|
+
def skip_security_checks
|
28
|
+
self.security_filters = []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Run all configured security checks
|
35
|
+
def run_security_checks
|
36
|
+
self.class.security_filters.each do |filter_config|
|
37
|
+
next unless should_run_security_check?(filter_config)
|
38
|
+
|
39
|
+
execute_security_check(filter_config[:check])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Determine if a security check should run based on conditions
|
44
|
+
def should_run_security_check?(filter_config)
|
45
|
+
return false unless check_only_condition(filter_config[:only])
|
46
|
+
return false if check_except_condition(filter_config[:except])
|
47
|
+
return false unless check_if_condition(filter_config[:if])
|
48
|
+
return false if check_unless_condition(filter_config[:unless])
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_only_condition(only_condition)
|
54
|
+
return true unless only_condition
|
55
|
+
|
56
|
+
only_actions = Array(only_condition).map(&:to_s)
|
57
|
+
only_actions.include?('action')
|
58
|
+
end
|
59
|
+
|
60
|
+
def check_except_condition(except_condition)
|
61
|
+
return false unless except_condition
|
62
|
+
|
63
|
+
except_actions = Array(except_condition).map(&:to_s)
|
64
|
+
except_actions.include?('action')
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_if_condition(if_condition)
|
68
|
+
return true unless if_condition
|
69
|
+
|
70
|
+
evaluate_condition(if_condition)
|
71
|
+
end
|
72
|
+
|
73
|
+
def check_unless_condition(unless_condition)
|
74
|
+
return false unless unless_condition
|
75
|
+
|
76
|
+
evaluate_condition(unless_condition)
|
77
|
+
end
|
78
|
+
|
79
|
+
def evaluate_condition(condition)
|
80
|
+
if condition.is_a?(Proc)
|
81
|
+
instance_exec(&condition)
|
82
|
+
elsif condition.is_a?(Symbol)
|
83
|
+
send(condition)
|
84
|
+
else
|
85
|
+
false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Execute a single security check
|
90
|
+
def execute_security_check(check)
|
91
|
+
if check.is_a?(Proc)
|
92
|
+
instance_exec(&check)
|
93
|
+
elsif check.is_a?(Symbol)
|
94
|
+
send(check)
|
95
|
+
else
|
96
|
+
raise ReactiveActions::SecurityCheckError, "Invalid security check: #{check.inspect}"
|
97
|
+
end
|
98
|
+
rescue ReactiveActions::Error
|
99
|
+
# Re-raise ReactiveActions errors as-is
|
100
|
+
raise
|
101
|
+
rescue StandardError => e
|
102
|
+
# Convert other errors to security errors
|
103
|
+
ReactiveActions.logger.error "Security check failed in #{self.class.name}: #{e.message}"
|
104
|
+
raise ReactiveActions::SecurityCheckError, "Security check failed: #{e.message}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# ReactiveActions
|
3
|
+
# ReactiveActions provides functionality for creating and executing reactive actions in Rails applications
|
4
4
|
module ReactiveActions
|
5
5
|
# Configuration class for ReactiveActions
|
6
|
-
# Manages settings for controller method delegation
|
6
|
+
# Manages settings for controller method delegation, instance variable delegation, and rate limiting
|
7
7
|
class Configuration
|
8
|
-
attr_accessor :delegated_controller_methods, :delegated_instance_variables
|
8
|
+
attr_accessor :delegated_controller_methods, :delegated_instance_variables, :global_rate_limit, :global_rate_limit_window, :rate_limit_key_generator, :rate_limit_cost_calculator
|
9
|
+
|
10
|
+
# Rate limiting configuration
|
11
|
+
attr_accessor :rate_limiting_enabled, :global_rate_limiting_enabled
|
9
12
|
|
10
13
|
def initialize
|
11
14
|
# Default methods to delegate
|
@@ -16,6 +19,23 @@ module ReactiveActions
|
|
16
19
|
|
17
20
|
# Default instance variables to delegate
|
18
21
|
@delegated_instance_variables = []
|
22
|
+
|
23
|
+
# Rate limiting defaults - DISABLED by default
|
24
|
+
@rate_limiting_enabled = false # Master switch for all rate limiting
|
25
|
+
@global_rate_limiting_enabled = false # Global controller-level rate limiting
|
26
|
+
@global_rate_limit = 600 # 600 requests per minute (10/second)
|
27
|
+
@global_rate_limit_window = 1.minute # per minute for responsive rate limiting
|
28
|
+
@rate_limit_key_generator = nil # Use default logic if nil
|
29
|
+
@rate_limit_cost_calculator = nil # Use default cost of 1 if nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Helper methods for checking rate limiting status
|
33
|
+
def rate_limiting_available?
|
34
|
+
@rate_limiting_enabled
|
35
|
+
end
|
36
|
+
|
37
|
+
def global_rate_limiting_active?
|
38
|
+
@rate_limiting_enabled && @global_rate_limiting_enabled
|
19
39
|
end
|
20
40
|
end
|
21
41
|
|